aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/cpp')
-rw-r--r--test/cpp/codegen/BUILD77
-rw-r--r--test/cpp/codegen/codegen_test_full.cc2
-rw-r--r--test/cpp/codegen/compiler_test_golden60
-rw-r--r--test/cpp/codegen/golden_file_test.cc12
-rw-r--r--test/cpp/codegen/proto_utils_test.cc93
-rw-r--r--test/cpp/common/BUILD36
-rw-r--r--test/cpp/common/alarm_cpp_test.cc18
-rw-r--r--test/cpp/common/auth_property_iterator_test.cc4
-rw-r--r--test/cpp/common/channel_arguments_test.cc76
-rw-r--r--test/cpp/common/channel_filter_test.cc80
-rw-r--r--test/cpp/end2end/BUILD323
-rw-r--r--test/cpp/end2end/async_end2end_test.cc74
-rw-r--r--test/cpp/end2end/client_crash_test_server.cc9
-rw-r--r--test/cpp/end2end/end2end_test.cc134
-rw-r--r--test/cpp/end2end/filter_end2end_test.cc29
-rw-r--r--test/cpp/end2end/generic_end2end_test.cc4
-rw-r--r--test/cpp/end2end/health_service_end2end_test.cc323
-rw-r--r--test/cpp/end2end/hybrid_end2end_test.cc173
-rw-r--r--test/cpp/end2end/mock_test.cc62
-rw-r--r--test/cpp/end2end/proto_server_reflection_test.cc2
-rw-r--r--test/cpp/end2end/round_robin_end2end_test.cc216
-rw-r--r--test/cpp/end2end/server_builder_plugin_test.cc29
-rw-r--r--test/cpp/end2end/server_crash_test.cc15
-rw-r--r--test/cpp/end2end/shutdown_test.cc43
-rw-r--r--test/cpp/end2end/streaming_throughput_test.cc10
-rw-r--r--test/cpp/end2end/test_service_impl.cc16
-rw-r--r--test/cpp/end2end/test_service_impl.h12
-rw-r--r--test/cpp/end2end/thread_stress_test.cc97
-rw-r--r--test/cpp/grpclb/grpclb_api_test.cc20
-rw-r--r--test/cpp/grpclb/grpclb_test.cc279
-rw-r--r--test/cpp/interop/client.cc237
-rw-r--r--test/cpp/interop/client_helper.cc12
-rw-r--r--test/cpp/interop/http2_client.cc244
-rw-r--r--test/cpp/interop/http2_client.h (renamed from test/cpp/qps/limit_cores.cc)78
-rw-r--r--test/cpp/interop/interop_client.cc76
-rw-r--r--test/cpp/interop/interop_client.h3
-rw-r--r--test/cpp/interop/interop_server.cc22
-rw-r--r--test/cpp/interop/interop_server_bootstrap.cc5
-rw-r--r--test/cpp/interop/interop_test.cc29
-rw-r--r--test/cpp/interop/reconnect_interop_client.cc2
-rw-r--r--test/cpp/interop/reconnect_interop_server.cc1
-rw-r--r--test/cpp/interop/server_helper.cc18
-rw-r--r--test/cpp/interop/server_helper.h6
-rw-r--r--test/cpp/interop/stress_test.cc31
-rw-r--r--test/cpp/microbenchmarks/bm_call_create.cc409
-rw-r--r--test/cpp/microbenchmarks/bm_chttp2_hpack.cc443
-rw-r--r--test/cpp/microbenchmarks/bm_closure.cc464
-rw-r--r--test/cpp/microbenchmarks/bm_cq.cc145
-rw-r--r--test/cpp/microbenchmarks/bm_error.cc248
-rw-r--r--test/cpp/microbenchmarks/bm_fullstack.cc1079
-rw-r--r--test/cpp/microbenchmarks/bm_metadata.cc297
-rw-r--r--test/cpp/microbenchmarks/noop-benchmark.cc (renamed from test/cpp/qps/limit_cores.h)26
-rw-r--r--test/cpp/microbenchmarks/representative_server_initial_metadata.headers4
-rw-r--r--test/cpp/microbenchmarks/representative_server_trailing_metadata.headers3
-rw-r--r--test/cpp/performance/writes_per_rpc_test.cc268
-rw-r--r--test/cpp/qps/client.h80
-rw-r--r--test/cpp/qps/client_async.cc53
-rw-r--r--test/cpp/qps/client_sync.cc34
-rw-r--r--test/cpp/qps/driver.cc258
-rw-r--r--test/cpp/qps/driver.h3
-rwxr-xr-xtest/cpp/qps/gen_build_yaml.py65
-rw-r--r--test/cpp/qps/interarrival.h6
-rw-r--r--test/cpp/qps/json_run_localhost.cc73
-rw-r--r--test/cpp/qps/qps_json_driver.cc148
-rw-r--r--test/cpp/qps/qps_openloop_test.cc2
-rw-r--r--test/cpp/qps/qps_test_with_poll.cc85
-rw-r--r--test/cpp/qps/qps_worker.cc20
-rw-r--r--test/cpp/qps/report.cc21
-rw-r--r--test/cpp/qps/report.h30
-rw-r--r--test/cpp/qps/server.h5
-rw-r--r--test/cpp/qps/server_async.cc41
-rw-r--r--test/cpp/qps/server_sync.cc16
-rw-r--r--test/cpp/qps/usage_timer.cc60
-rw-r--r--test/cpp/qps/usage_timer.h2
-rw-r--r--test/cpp/qps/worker.cc2
-rw-r--r--test/cpp/test/server_context_test_spouse_test.cc111
-rw-r--r--test/cpp/thread_manager/thread_manager_test.cc136
-rw-r--r--test/cpp/util/BUILD85
-rw-r--r--test/cpp/util/byte_buffer_proto_helper.cc2
-rw-r--r--test/cpp/util/byte_buffer_test.cc28
-rw-r--r--test/cpp/util/cli_call.cc179
-rw-r--r--test/cpp/util/cli_call.h53
-rw-r--r--test/cpp/util/cli_call_test.cc6
-rw-r--r--test/cpp/util/config_grpc_cli.h2
-rw-r--r--test/cpp/util/create_test_channel.cc64
-rw-r--r--test/cpp/util/create_test_channel.h4
-rw-r--r--test/cpp/util/grpc_cli.cc6
-rw-r--r--test/cpp/util/grpc_tool.cc365
-rw-r--r--test/cpp/util/grpc_tool_test.cc256
-rw-r--r--test/cpp/util/metrics_server.h6
-rw-r--r--test/cpp/util/proto_file_parser.cc46
-rw-r--r--test/cpp/util/proto_file_parser.h7
-rw-r--r--test/cpp/util/proto_reflection_descriptor_database.h16
-rw-r--r--test/cpp/util/slice_test.cc18
-rw-r--r--test/cpp/util/test_credentials_provider.cc78
-rw-r--r--test/cpp/util/test_credentials_provider.h50
96 files changed, 7731 insertions, 1269 deletions
diff --git a/test/cpp/codegen/BUILD b/test/cpp/codegen/BUILD
new file mode 100644
index 0000000000..14d5733da2
--- /dev/null
+++ b/test/cpp/codegen/BUILD
@@ -0,0 +1,77 @@
+# Copyright 2017, 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.
+
+licenses(["notice"]) # 3-clause BSD
+
+cc_test(
+ name = "codegen_test_full",
+ srcs = ["codegen_test_full.cc"],
+ deps = [
+ "//:grpc++",
+ "//external:gtest",
+ "//test/core/util:gpr_test_util",
+ ],
+)
+
+cc_test(
+ name = "codegen_test_minimal",
+ srcs = ["codegen_test_minimal.cc"],
+ deps = [
+ "//:grpc++",
+ "//external:gtest",
+ "//test/core/util:gpr_test_util",
+ ],
+)
+
+cc_test(
+ name = "proto_utils_test",
+ srcs = ["proto_utils_test.cc"],
+ deps = [
+ "//:grpc++",
+ "//external:gtest",
+ "//test/core/util:gpr_test_util",
+ ],
+)
+
+cc_test(
+ name = "golden_file_test",
+ srcs = ["golden_file_test.cc"],
+ args = ["--generated_file_path=$(GENDIR)/src/proto/grpc/testing/compiler_test.grpc.pb.h"],
+ data = [
+ ":compiler_test_golden",
+ "//src/proto/grpc/testing:_compiler_test_proto_grpc_codegen",
+ ],
+ deps = [
+ "//:grpc++",
+ "//external:gflags",
+ "//external:gtest",
+ "//src/proto/grpc/testing:compiler_test_proto",
+ "//test/core/util:gpr_test_util",
+ ],
+)
diff --git a/test/cpp/codegen/codegen_test_full.cc b/test/cpp/codegen/codegen_test_full.cc
index d6e2416b55..bc19fc9669 100644
--- a/test/cpp/codegen/codegen_test_full.cc
+++ b/test/cpp/codegen/codegen_test_full.cc
@@ -1,6 +1,6 @@
/*
*
- * Copyright 2016, Google Inc.
+ * Copyright 2017, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/test/cpp/codegen/compiler_test_golden b/test/cpp/codegen/compiler_test_golden
index 2aba036f8f..fd26a17ac1 100644
--- a/test/cpp/codegen/compiler_test_golden
+++ b/test/cpp/codegen/compiler_test_golden
@@ -67,7 +67,7 @@ namespace testing {
// ServiceA detached comment 2
//
// ServiceA leading comment 1
-class ServiceA GRPC_FINAL {
+class ServiceA final {
public:
class StubInterface {
public:
@@ -94,10 +94,10 @@ class ServiceA GRPC_FINAL {
virtual ::grpc::ClientWriterInterface< ::grpc::testing::Request>* MethodA2Raw(::grpc::ClientContext* context, ::grpc::testing::Response* response) = 0;
virtual ::grpc::ClientAsyncWriterInterface< ::grpc::testing::Request>* AsyncMethodA2Raw(::grpc::ClientContext* context, ::grpc::testing::Response* response, ::grpc::CompletionQueue* cq, void* tag) = 0;
};
- class Stub GRPC_FINAL : public StubInterface {
+ class Stub final : public StubInterface {
public:
Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);
- ::grpc::Status MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::testing::Response* response) GRPC_OVERRIDE;
+ ::grpc::Status MethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::testing::Response* response) override;
std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>> AsyncMethodA1(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) {
return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>>(AsyncMethodA1Raw(context, request, cq));
}
@@ -110,9 +110,9 @@ class ServiceA GRPC_FINAL {
private:
std::shared_ptr< ::grpc::ChannelInterface> channel_;
- ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* AsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) GRPC_OVERRIDE;
- ::grpc::ClientWriter< ::grpc::testing::Request>* MethodA2Raw(::grpc::ClientContext* context, ::grpc::testing::Response* response) GRPC_OVERRIDE;
- ::grpc::ClientAsyncWriter< ::grpc::testing::Request>* AsyncMethodA2Raw(::grpc::ClientContext* context, ::grpc::testing::Response* response, ::grpc::CompletionQueue* cq, void* tag) GRPC_OVERRIDE;
+ ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* AsyncMethodA1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override;
+ ::grpc::ClientWriter< ::grpc::testing::Request>* MethodA2Raw(::grpc::ClientContext* context, ::grpc::testing::Response* response) override;
+ ::grpc::ClientAsyncWriter< ::grpc::testing::Request>* AsyncMethodA2Raw(::grpc::ClientContext* context, ::grpc::testing::Response* response, ::grpc::CompletionQueue* cq, void* tag) override;
const ::grpc::RpcMethod rpcmethod_MethodA1_;
const ::grpc::RpcMethod rpcmethod_MethodA2_;
};
@@ -140,11 +140,11 @@ class ServiceA GRPC_FINAL {
WithAsyncMethod_MethodA1() {
::grpc::Service::MarkMethodAsync(0);
}
- ~WithAsyncMethod_MethodA1() GRPC_OVERRIDE {
+ ~WithAsyncMethod_MethodA1() override {
BaseClassMustBeDerivedFromService(this);
}
// disable synchronous version of this method
- ::grpc::Status MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+ ::grpc::Status MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) final override {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
@@ -160,11 +160,11 @@ class ServiceA GRPC_FINAL {
WithAsyncMethod_MethodA2() {
::grpc::Service::MarkMethodAsync(1);
}
- ~WithAsyncMethod_MethodA2() GRPC_OVERRIDE {
+ ~WithAsyncMethod_MethodA2() override {
BaseClassMustBeDerivedFromService(this);
}
// disable synchronous version of this method
- ::grpc::Status MethodA2(::grpc::ServerContext* context, ::grpc::ServerReader< ::grpc::testing::Request>* reader, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+ ::grpc::Status MethodA2(::grpc::ServerContext* context, ::grpc::ServerReader< ::grpc::testing::Request>* reader, ::grpc::testing::Response* response) final override {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
@@ -181,11 +181,11 @@ class ServiceA GRPC_FINAL {
WithGenericMethod_MethodA1() {
::grpc::Service::MarkMethodGeneric(0);
}
- ~WithGenericMethod_MethodA1() GRPC_OVERRIDE {
+ ~WithGenericMethod_MethodA1() override {
BaseClassMustBeDerivedFromService(this);
}
// disable synchronous version of this method
- ::grpc::Status MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+ ::grpc::Status MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) final override {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
@@ -198,11 +198,11 @@ class ServiceA GRPC_FINAL {
WithGenericMethod_MethodA2() {
::grpc::Service::MarkMethodGeneric(1);
}
- ~WithGenericMethod_MethodA2() GRPC_OVERRIDE {
+ ~WithGenericMethod_MethodA2() override {
BaseClassMustBeDerivedFromService(this);
}
// disable synchronous version of this method
- ::grpc::Status MethodA2(::grpc::ServerContext* context, ::grpc::ServerReader< ::grpc::testing::Request>* reader, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+ ::grpc::Status MethodA2(::grpc::ServerContext* context, ::grpc::ServerReader< ::grpc::testing::Request>* reader, ::grpc::testing::Response* response) final override {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
@@ -213,14 +213,14 @@ class ServiceA GRPC_FINAL {
void BaseClassMustBeDerivedFromService(const Service *service) {}
public:
WithStreamedUnaryMethod_MethodA1() {
- ::grpc::Service::MarkMethodStreamedUnary(0,
+ ::grpc::Service::MarkMethodStreamed(0,
new ::grpc::StreamedUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>(std::bind(&WithStreamedUnaryMethod_MethodA1<BaseClass>::StreamedMethodA1, this, std::placeholders::_1, std::placeholders::_2)));
}
- ~WithStreamedUnaryMethod_MethodA1() GRPC_OVERRIDE {
+ ~WithStreamedUnaryMethod_MethodA1() override {
BaseClassMustBeDerivedFromService(this);
}
// disable regular version of this method
- ::grpc::Status MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+ ::grpc::Status MethodA1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) final override {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
@@ -228,10 +228,12 @@ class ServiceA GRPC_FINAL {
virtual ::grpc::Status StreamedMethodA1(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::grpc::testing::Request,::grpc::testing::Response>* server_unary_streamer) = 0;
};
typedef WithStreamedUnaryMethod_MethodA1<Service > StreamedUnaryService;
+ typedef Service SplitStreamedService;
+ typedef WithStreamedUnaryMethod_MethodA1<Service > StreamedService;
};
// ServiceB leading comment 1
-class ServiceB GRPC_FINAL {
+class ServiceB final {
public:
class StubInterface {
public:
@@ -245,17 +247,17 @@ class ServiceB GRPC_FINAL {
private:
virtual ::grpc::ClientAsyncResponseReaderInterface< ::grpc::testing::Response>* AsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) = 0;
};
- class Stub GRPC_FINAL : public StubInterface {
+ class Stub final : public StubInterface {
public:
Stub(const std::shared_ptr< ::grpc::ChannelInterface>& channel);
- ::grpc::Status MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::testing::Response* response) GRPC_OVERRIDE;
+ ::grpc::Status MethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::testing::Response* response) override;
std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>> AsyncMethodB1(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) {
return std::unique_ptr< ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>>(AsyncMethodB1Raw(context, request, cq));
}
private:
std::shared_ptr< ::grpc::ChannelInterface> channel_;
- ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* AsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) GRPC_OVERRIDE;
+ ::grpc::ClientAsyncResponseReader< ::grpc::testing::Response>* AsyncMethodB1Raw(::grpc::ClientContext* context, const ::grpc::testing::Request& request, ::grpc::CompletionQueue* cq) override;
const ::grpc::RpcMethod rpcmethod_MethodB1_;
};
static std::unique_ptr<Stub> NewStub(const std::shared_ptr< ::grpc::ChannelInterface>& channel, const ::grpc::StubOptions& options = ::grpc::StubOptions());
@@ -276,11 +278,11 @@ class ServiceB GRPC_FINAL {
WithAsyncMethod_MethodB1() {
::grpc::Service::MarkMethodAsync(0);
}
- ~WithAsyncMethod_MethodB1() GRPC_OVERRIDE {
+ ~WithAsyncMethod_MethodB1() override {
BaseClassMustBeDerivedFromService(this);
}
// disable synchronous version of this method
- ::grpc::Status MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+ ::grpc::Status MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) final override {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
@@ -297,11 +299,11 @@ class ServiceB GRPC_FINAL {
WithGenericMethod_MethodB1() {
::grpc::Service::MarkMethodGeneric(0);
}
- ~WithGenericMethod_MethodB1() GRPC_OVERRIDE {
+ ~WithGenericMethod_MethodB1() override {
BaseClassMustBeDerivedFromService(this);
}
// disable synchronous version of this method
- ::grpc::Status MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+ ::grpc::Status MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) final override {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
@@ -312,14 +314,14 @@ class ServiceB GRPC_FINAL {
void BaseClassMustBeDerivedFromService(const Service *service) {}
public:
WithStreamedUnaryMethod_MethodB1() {
- ::grpc::Service::MarkMethodStreamedUnary(0,
+ ::grpc::Service::MarkMethodStreamed(0,
new ::grpc::StreamedUnaryHandler< ::grpc::testing::Request, ::grpc::testing::Response>(std::bind(&WithStreamedUnaryMethod_MethodB1<BaseClass>::StreamedMethodB1, this, std::placeholders::_1, std::placeholders::_2)));
}
- ~WithStreamedUnaryMethod_MethodB1() GRPC_OVERRIDE {
+ ~WithStreamedUnaryMethod_MethodB1() override {
BaseClassMustBeDerivedFromService(this);
}
// disable regular version of this method
- ::grpc::Status MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) GRPC_FINAL GRPC_OVERRIDE {
+ ::grpc::Status MethodB1(::grpc::ServerContext* context, const ::grpc::testing::Request* request, ::grpc::testing::Response* response) final override {
abort();
return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "");
}
@@ -327,6 +329,8 @@ class ServiceB GRPC_FINAL {
virtual ::grpc::Status StreamedMethodB1(::grpc::ServerContext* context, ::grpc::ServerUnaryStreamer< ::grpc::testing::Request,::grpc::testing::Response>* server_unary_streamer) = 0;
};
typedef WithStreamedUnaryMethod_MethodB1<Service > StreamedUnaryService;
+ typedef Service SplitStreamedService;
+ typedef WithStreamedUnaryMethod_MethodB1<Service > StreamedService;
};
// ServiceB trailing comment 1
diff --git a/test/cpp/codegen/golden_file_test.cc b/test/cpp/codegen/golden_file_test.cc
index ec08d08de6..158a4d933c 100644
--- a/test/cpp/codegen/golden_file_test.cc
+++ b/test/cpp/codegen/golden_file_test.cc
@@ -34,15 +34,18 @@
#include <fstream>
#include <sstream>
+#include <gflags/gflags.h>
#include <gtest/gtest.h>
-// These paths rely on the fact that we run our tests under grpc/
-const char kGeneratedFilePath[] =
- "gens/src/proto/grpc/testing/compiler_test.grpc.pb.h";
+DEFINE_string(generated_file_path, "",
+ "path to the generated compiler_test.grpc.pb.h file");
+
const char kGoldenFilePath[] = "test/cpp/codegen/compiler_test_golden";
TEST(GoldenFileTest, TestGeneratedFile) {
- std::ifstream generated(kGeneratedFilePath);
+ ASSERT_FALSE(FLAGS_generated_file_path.empty());
+
+ std::ifstream generated(FLAGS_generated_file_path);
std::ifstream golden(kGoldenFilePath);
ASSERT_TRUE(generated.good());
@@ -60,5 +63,6 @@ TEST(GoldenFileTest, TestGeneratedFile) {
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
+ ::google::ParseCommandLineFlags(&argc, &argv, true);
return RUN_ALL_TESTS();
}
diff --git a/test/cpp/codegen/proto_utils_test.cc b/test/cpp/codegen/proto_utils_test.cc
new file mode 100644
index 0000000000..1daa142b50
--- /dev/null
+++ b/test/cpp/codegen/proto_utils_test.cc
@@ -0,0 +1,93 @@
+/*
+ *
+ * Copyright 2017, 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 <grpc++/impl/codegen/proto_utils.h>
+#include <grpc++/impl/grpc_library.h>
+#include <gtest/gtest.h>
+
+namespace grpc {
+namespace internal {
+
+static GrpcLibraryInitializer g_gli_initializer;
+
+// Provide access to GrpcBufferWriter internals.
+class GrpcBufferWriterPeer {
+ public:
+ explicit GrpcBufferWriterPeer(internal::GrpcBufferWriter* writer)
+ : writer_(writer) {}
+ bool have_backup() const { return writer_->have_backup_; }
+ const grpc_slice& backup_slice() const { return writer_->backup_slice_; }
+ const grpc_slice& slice() const { return writer_->slice_; }
+
+ private:
+ GrpcBufferWriter* writer_;
+};
+
+class ProtoUtilsTest : public ::testing::Test {};
+
+// Regression test for a memory corruption bug where a series of
+// GrpcBufferWriter Next()/Backup() invocations could result in a dangling
+// pointer returned by Next() due to the interaction between grpc_slice inlining
+// and GRPC_SLICE_START_PTR.
+TEST_F(ProtoUtilsTest, BackupNext) {
+ // Ensure the GrpcBufferWriter internals are initialized.
+ g_gli_initializer.summon();
+
+ grpc_byte_buffer* bp;
+ GrpcBufferWriter writer(&bp, 8192);
+ GrpcBufferWriterPeer peer(&writer);
+
+ void* data;
+ int size;
+ // Allocate a slice.
+ ASSERT_TRUE(writer.Next(&data, &size));
+ EXPECT_EQ(8192, size);
+ // Return a single byte. Before the fix that this test acts as a regression
+ // for, this would have resulted in an inlined backup slice.
+ writer.BackUp(1);
+ EXPECT_TRUE(!peer.have_backup());
+ // On the next allocation, the slice is non-inlined.
+ ASSERT_TRUE(writer.Next(&data, &size));
+ EXPECT_TRUE(peer.slice().refcount != NULL);
+
+ // Cleanup.
+ g_core_codegen_interface->grpc_byte_buffer_destroy(bp);
+}
+
+} // namespace internal
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/common/BUILD b/test/cpp/common/BUILD
new file mode 100644
index 0000000000..0e2db00f0a
--- /dev/null
+++ b/test/cpp/common/BUILD
@@ -0,0 +1,36 @@
+# Copyright 2017, 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.
+
+licenses(["notice"]) # 3-clause BSD
+
+cc_test(
+ name = "alarm_cpp_test",
+ srcs = ["alarm_cpp_test.cc"],
+ deps = ["//:grpc++", "//external:gtest", "//test/core/util:gpr_test_util"],
+)
diff --git a/test/cpp/common/alarm_cpp_test.cc b/test/cpp/common/alarm_cpp_test.cc
index a05ac30b1c..41085174a4 100644
--- a/test/cpp/common/alarm_cpp_test.cc
+++ b/test/cpp/common/alarm_cpp_test.cc
@@ -43,12 +43,12 @@ namespace {
TEST(AlarmTest, RegularExpiry) {
CompletionQueue cq;
void* junk = reinterpret_cast<void*>(1618033);
- Alarm alarm(&cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1), junk);
+ Alarm alarm(&cq, grpc_timeout_seconds_to_deadline(1), junk);
void* output_tag;
bool ok;
const CompletionQueue::NextStatus status = cq.AsyncNext(
- (void**)&output_tag, &ok, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(2));
+ (void**)&output_tag, &ok, grpc_timeout_seconds_to_deadline(2));
EXPECT_EQ(status, CompletionQueue::GOT_EVENT);
EXPECT_TRUE(ok);
@@ -65,7 +65,7 @@ TEST(AlarmTest, RegularExpiryChrono) {
void* output_tag;
bool ok;
const CompletionQueue::NextStatus status = cq.AsyncNext(
- (void**)&output_tag, &ok, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(2));
+ (void**)&output_tag, &ok, grpc_timeout_seconds_to_deadline(2));
EXPECT_EQ(status, CompletionQueue::GOT_EVENT);
EXPECT_TRUE(ok);
@@ -75,12 +75,12 @@ TEST(AlarmTest, RegularExpiryChrono) {
TEST(AlarmTest, ZeroExpiry) {
CompletionQueue cq;
void* junk = reinterpret_cast<void*>(1618033);
- Alarm alarm(&cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(0), junk);
+ Alarm alarm(&cq, grpc_timeout_seconds_to_deadline(0), junk);
void* output_tag;
bool ok;
const CompletionQueue::NextStatus status = cq.AsyncNext(
- (void**)&output_tag, &ok, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(0));
+ (void**)&output_tag, &ok, grpc_timeout_seconds_to_deadline(0));
EXPECT_EQ(status, CompletionQueue::GOT_EVENT);
EXPECT_TRUE(ok);
@@ -90,12 +90,12 @@ TEST(AlarmTest, ZeroExpiry) {
TEST(AlarmTest, NegativeExpiry) {
CompletionQueue cq;
void* junk = reinterpret_cast<void*>(1618033);
- Alarm alarm(&cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(-1), junk);
+ Alarm alarm(&cq, grpc_timeout_seconds_to_deadline(-1), junk);
void* output_tag;
bool ok;
const CompletionQueue::NextStatus status = cq.AsyncNext(
- (void**)&output_tag, &ok, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(0));
+ (void**)&output_tag, &ok, grpc_timeout_seconds_to_deadline(0));
EXPECT_EQ(status, CompletionQueue::GOT_EVENT);
EXPECT_TRUE(ok);
@@ -105,13 +105,13 @@ TEST(AlarmTest, NegativeExpiry) {
TEST(AlarmTest, Cancellation) {
CompletionQueue cq;
void* junk = reinterpret_cast<void*>(1618033);
- Alarm alarm(&cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(2), junk);
+ Alarm alarm(&cq, grpc_timeout_seconds_to_deadline(2), junk);
alarm.Cancel();
void* output_tag;
bool ok;
const CompletionQueue::NextStatus status = cq.AsyncNext(
- (void**)&output_tag, &ok, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(1));
+ (void**)&output_tag, &ok, grpc_timeout_seconds_to_deadline(1));
EXPECT_EQ(status, CompletionQueue::GOT_EVENT);
EXPECT_FALSE(ok);
diff --git a/test/cpp/common/auth_property_iterator_test.cc b/test/cpp/common/auth_property_iterator_test.cc
index 66225ff335..16bebcab74 100644
--- a/test/cpp/common/auth_property_iterator_test.cc
+++ b/test/cpp/common/auth_property_iterator_test.cc
@@ -56,7 +56,7 @@ class TestAuthPropertyIterator : public AuthPropertyIterator {
class AuthPropertyIteratorTest : public ::testing::Test {
protected:
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
ctx_ = grpc_auth_context_create(NULL);
grpc_auth_context_add_cstring_property(ctx_, "name", "chapi");
grpc_auth_context_add_cstring_property(ctx_, "name", "chapo");
@@ -64,7 +64,7 @@ class AuthPropertyIteratorTest : public ::testing::Test {
EXPECT_EQ(1,
grpc_auth_context_set_peer_identity_property_name(ctx_, "name"));
}
- void TearDown() GRPC_OVERRIDE { grpc_auth_context_release(ctx_); }
+ void TearDown() override { grpc_auth_context_release(ctx_); }
grpc_auth_context* ctx_;
};
diff --git a/test/cpp/common/channel_arguments_test.cc b/test/cpp/common/channel_arguments_test.cc
index 1443eb2f68..190d32ce06 100644
--- a/test/cpp/common/channel_arguments_test.cc
+++ b/test/cpp/common/channel_arguments_test.cc
@@ -33,12 +33,62 @@
#include <grpc++/support/channel_arguments.h>
+#include <grpc++/grpc++.h>
#include <grpc/grpc.h>
+#include <grpc/support/useful.h>
#include <gtest/gtest.h>
+extern "C" {
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/socket_mutator.h"
+}
+
namespace grpc {
namespace testing {
+namespace {
+
+// A simple grpc_socket_mutator to be used to test SetSocketMutator
+class TestSocketMutator : public grpc_socket_mutator {
+ public:
+ TestSocketMutator();
+
+ bool MutateFd(int fd) {
+ // Do nothing on the fd
+ return true;
+ }
+};
+
+//
+// C API for TestSocketMutator
+//
+
+bool test_mutator_mutate_fd(int fd, grpc_socket_mutator* mutator) {
+ TestSocketMutator* tsm = (TestSocketMutator*)mutator;
+ return tsm->MutateFd(fd);
+}
+
+int test_mutator_compare(grpc_socket_mutator* a, grpc_socket_mutator* b) {
+ return GPR_ICMP(a, b);
+}
+
+void test_mutator_destroy(grpc_socket_mutator* mutator) {
+ TestSocketMutator* tsm = (TestSocketMutator*)mutator;
+ delete tsm;
+}
+
+grpc_socket_mutator_vtable test_mutator_vtable = {
+ test_mutator_mutate_fd, test_mutator_compare, test_mutator_destroy};
+
+//
+// TestSocketMutator implementation
+//
+
+TestSocketMutator::TestSocketMutator() {
+ grpc_socket_mutator_init(this, &test_mutator_vtable);
+}
+}
+
class ChannelArgumentsTest : public ::testing::Test {
protected:
ChannelArgumentsTest()
@@ -53,7 +103,7 @@ class ChannelArgumentsTest : public ::testing::Test {
grpc::string GetDefaultUserAgentPrefix() {
std::ostringstream user_agent_prefix;
- user_agent_prefix << "grpc-c++/" << grpc_version_string();
+ user_agent_prefix << "grpc-c++/" << Version();
return user_agent_prefix.str();
}
@@ -165,6 +215,30 @@ TEST_F(ChannelArgumentsTest, SetPointer) {
EXPECT_TRUE(HasArg(arg0));
}
+TEST_F(ChannelArgumentsTest, SetSocketMutator) {
+ VerifyDefaultChannelArgs();
+ grpc_arg arg0, arg1;
+ TestSocketMutator* mutator0 = new TestSocketMutator();
+ TestSocketMutator* mutator1 = new TestSocketMutator();
+ arg0 = grpc_socket_mutator_to_arg(mutator0);
+ arg1 = grpc_socket_mutator_to_arg(mutator1);
+
+ channel_args_.SetSocketMutator(mutator0);
+ EXPECT_TRUE(HasArg(arg0));
+
+ channel_args_.SetSocketMutator(mutator1);
+ EXPECT_TRUE(HasArg(arg1));
+ // arg0 is replaced by arg1
+ EXPECT_FALSE(HasArg(arg0));
+
+ // arg0 is destroyed by grpc_socket_mutator_to_arg(mutator1)
+ {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ arg1.value.pointer.vtable->destroy(&exec_ctx, arg1.value.pointer.p);
+ grpc_exec_ctx_finish(&exec_ctx);
+ }
+}
+
TEST_F(ChannelArgumentsTest, SetUserAgentPrefix) {
VerifyDefaultChannelArgs();
grpc::string prefix("prefix");
diff --git a/test/cpp/common/channel_filter_test.cc b/test/cpp/common/channel_filter_test.cc
new file mode 100644
index 0000000000..d78b05e5d8
--- /dev/null
+++ b/test/cpp/common/channel_filter_test.cc
@@ -0,0 +1,80 @@
+//
+// 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 "src/cpp/common/channel_filter.h"
+
+#include <limits.h>
+
+#include <grpc/grpc.h>
+#include <gtest/gtest.h>
+
+namespace grpc {
+namespace testing {
+
+class MyChannelData : public ChannelData {
+ public:
+ MyChannelData() {}
+
+ grpc_error* Init(grpc_exec_ctx* exec_ctx,
+ grpc_channel_element_args* args) override {
+ (void)args->channel_args; // Make sure field is available.
+ return GRPC_ERROR_NONE;
+ }
+};
+
+class MyCallData : public CallData {
+ public:
+ MyCallData() {}
+
+ grpc_error* Init(grpc_exec_ctx* exec_ctx, ChannelData* channel_data,
+ const grpc_call_element_args* args) override {
+ (void)args->path; // Make sure field is available.
+ return GRPC_ERROR_NONE;
+ }
+};
+
+// This test ensures that when we make changes to the filter API in
+// C-core, we don't accidentally break the C++ filter API.
+TEST(ChannelFilterTest, RegisterChannelFilter) {
+ grpc::RegisterChannelFilter<MyChannelData, MyCallData>(
+ "myfilter", GRPC_CLIENT_CHANNEL, INT_MAX, nullptr);
+}
+
+// TODO(roth): When we have time, add tests for all methods of the
+// filter API.
+
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD
new file mode 100644
index 0000000000..0bf7948fcf
--- /dev/null
+++ b/test/cpp/end2end/BUILD
@@ -0,0 +1,323 @@
+# Copyright 2017, 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.
+
+licenses(["notice"]) # 3-clause BSD
+
+cc_library(
+ name = "test_service_impl",
+ srcs = ["test_service_impl.cc"],
+ hdrs = ["test_service_impl.h"],
+ deps = [
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_proto",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "async_end2end_test",
+ srcs = ["async_end2end_test.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "client_crash_test",
+ srcs = ["client_crash_test.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "client_crash_test_server",
+ srcs = ["client_crash_test_server.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gflags",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "end2end_test",
+ srcs = ["end2end_test.cc"],
+ deps = [
+ ":test_service_impl",
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "filter_end2end_test",
+ srcs = ["filter_end2end_test.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "generic_end2end_test",
+ srcs = ["generic_end2end_test.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "hybrid_end2end_test",
+ srcs = ["hybrid_end2end_test.cc"],
+ deps = [
+ ":test_service_impl",
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "mock_test",
+ srcs = ["mock_test.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "round_robin_end2end_test",
+ srcs = ["round_robin_end2end_test.cc"],
+ deps = [
+ ":test_service_impl",
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "proto_server_reflection_test",
+ srcs = ["proto_server_reflection_test.cc"],
+ deps = [
+ ":test_service_impl",
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//:grpc++_reflection",
+ "//external:gflags",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:grpc++_proto_reflection_desc_db",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "server_builder_plugin_test",
+ srcs = ["server_builder_plugin_test.cc"],
+ deps = [
+ ":test_service_impl",
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "server_crash_test",
+ srcs = ["server_crash_test.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "server_crash_test_client",
+ srcs = ["server_crash_test_client.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gflags",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "shutdown_test",
+ srcs = ["shutdown_test.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "streaming_throughput_test",
+ srcs = ["streaming_throughput_test.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
+
+cc_test(
+ name = "thread_stress_test",
+ srcs = ["thread_stress_test.cc"],
+ deps = [
+ "//:gpr",
+ "//:grpc",
+ "//:grpc++",
+ "//external:gtest",
+ "//src/proto/grpc/testing:echo_messages_proto",
+ "//src/proto/grpc/testing:echo_proto",
+ "//src/proto/grpc/testing/duplicate:echo_duplicate_proto",
+ "//test/core/util:gpr_test_util",
+ "//test/core/util:grpc_test_util",
+ "//test/cpp/util:test_util",
+ ],
+)
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc
index a9b7c8f8d3..32e8a41795 100644
--- a/test/cpp/end2end/async_end2end_test.cc
+++ b/test/cpp/end2end/async_end2end_test.cc
@@ -42,11 +42,13 @@
#include <grpc++/server_builder.h>
#include <grpc++/server_context.h>
#include <grpc/grpc.h>
+#include <grpc/support/log.h>
#include <grpc/support/thd.h>
#include <grpc/support/time.h>
#include <grpc/support/tls.h>
#include <gtest/gtest.h>
+#include "src/core/lib/iomgr/port.h"
#include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.h"
#include "src/proto/grpc/testing/echo.grpc.pb.h"
#include "test/core/util/port.h"
@@ -54,7 +56,7 @@
#include "test/cpp/util/string_ref_helper.h"
#include "test/cpp/util/test_credentials_provider.h"
-#ifdef GPR_POSIX_SOCKET
+#ifdef GRPC_POSIX_SOCKET
#include "src/core/lib/iomgr/ev_posix.h"
#endif
@@ -73,7 +75,7 @@ namespace {
void* tag(int i) { return (void*)(intptr_t)i; }
int detag(void* p) { return static_cast<int>(reinterpret_cast<intptr_t>(p)); }
-#ifdef GPR_POSIX_SOCKET
+#ifdef GRPC_POSIX_SOCKET
static int maybe_assert_non_blocking_poll(struct pollfd* pfds, nfds_t nfds,
int timeout) {
if (gpr_tls_get(&g_is_async_end2end_test)) {
@@ -210,10 +212,10 @@ bool plugin_has_sync_methods(std::unique_ptr<ServerBuilderPlugin>& plugin) {
// that needs to be tested here.
class ServerBuilderSyncPluginDisabler : public ::grpc::ServerBuilderOption {
public:
- void UpdateArguments(ChannelArguments* arg) GRPC_OVERRIDE {}
+ void UpdateArguments(ChannelArguments* arg) override {}
- void UpdatePlugins(std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins)
- GRPC_OVERRIDE {
+ void UpdatePlugins(
+ std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) override {
plugins->erase(std::remove_if(plugins->begin(), plugins->end(),
plugin_has_sync_methods),
plugins->end());
@@ -227,12 +229,7 @@ class TestScenario {
: disable_blocking(non_block),
credentials_type(creds_type),
message_content(content) {}
- void Log() const {
- gpr_log(
- GPR_INFO,
- "Scenario: disable_blocking %d, credentials %s, message size %" PRIuPTR,
- disable_blocking, credentials_type.c_str(), message_content.size());
- }
+ void Log() const;
bool disable_blocking;
// Although the below grpc::string's are logically const, we can't declare
// them const because of a limitation in the way old compilers (e.g., gcc-4.4)
@@ -241,11 +238,25 @@ class TestScenario {
grpc::string message_content;
};
+static std::ostream& operator<<(std::ostream& out,
+ const TestScenario& scenario) {
+ return out << "TestScenario{disable_blocking="
+ << (scenario.disable_blocking ? "true" : "false")
+ << ", credentials='" << scenario.credentials_type
+ << "', message_size=" << scenario.message_content.size() << "}";
+}
+
+void TestScenario::Log() const {
+ std::ostringstream out;
+ out << *this;
+ gpr_log(GPR_DEBUG, "%s", out.str().c_str());
+}
+
class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> {
protected:
AsyncEnd2endTest() { GetParam().Log(); }
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
poll_overrider_.reset(new PollingOverrider(!GetParam().disable_blocking));
port_ = grpc_pick_unused_port_or_die();
@@ -253,7 +264,8 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> {
// Setup server
ServerBuilder builder;
- auto server_creds = GetServerCredentials(GetParam().credentials_type);
+ auto server_creds = GetCredentialsProvider()->GetServerCredentials(
+ GetParam().credentials_type);
builder.AddListeningPort(server_address_.str(), server_creds);
builder.RegisterService(&service_);
cq_ = builder.AddCompletionQueue();
@@ -268,7 +280,7 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> {
gpr_tls_set(&g_is_async_end2end_test, 1);
}
- void TearDown() GRPC_OVERRIDE {
+ void TearDown() override {
server_->Shutdown();
void* ignored_tag;
bool ignored_ok;
@@ -282,8 +294,8 @@ class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> {
void ResetStub() {
ChannelArguments args;
- auto channel_creds =
- GetChannelCredentials(GetParam().credentials_type, &args);
+ auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(
+ GetParam().credentials_type, &args);
std::shared_ptr<Channel> channel =
CreateCustomChannel(server_address_.str(), channel_creds, args);
stub_ = grpc::testing::EchoTestService::NewStub(channel);
@@ -351,15 +363,13 @@ void ServerWait(Server* server, int* notify) {
}
TEST_P(AsyncEnd2endTest, WaitAndShutdownTest) {
int notify = 0;
- std::thread* wait_thread =
- new std::thread(&ServerWait, server_.get(), &notify);
+ std::thread wait_thread(&ServerWait, server_.get(), &notify);
ResetStub();
SendRpc(1);
EXPECT_EQ(0, notify);
server_->Shutdown();
- wait_thread->join();
+ wait_thread.join();
EXPECT_EQ(1, notify);
- delete wait_thread;
}
TEST_P(AsyncEnd2endTest, ShutdownThenWait) {
@@ -893,8 +903,8 @@ TEST_P(AsyncEnd2endTest, ServerCheckDone) {
TEST_P(AsyncEnd2endTest, UnimplementedRpc) {
ChannelArguments args;
- auto channel_creds =
- GetChannelCredentials(GetParam().credentials_type, &args);
+ auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(
+ GetParam().credentials_type, &args);
std::shared_ptr<Channel> channel =
CreateCustomChannel(server_address_.str(), channel_creds, args);
std::unique_ptr<grpc::testing::UnimplementedEchoService::Stub> stub;
@@ -990,7 +1000,7 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest {
expected_server_cq_result = false;
}
- std::thread* server_try_cancel_thd = NULL;
+ std::thread* server_try_cancel_thd = nullptr;
auto verif = Verifier(GetParam().disable_blocking);
@@ -1026,7 +1036,7 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest {
}
}
- if (server_try_cancel_thd != NULL) {
+ if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
}
@@ -1111,7 +1121,7 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest {
expected_cq_result = false;
}
- std::thread* server_try_cancel_thd = NULL;
+ std::thread* server_try_cancel_thd = nullptr;
auto verif = Verifier(GetParam().disable_blocking);
@@ -1149,7 +1159,7 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest {
}
}
- if (server_try_cancel_thd != NULL) {
+ if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
}
@@ -1251,7 +1261,7 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest {
expected_cq_result = false;
}
- std::thread* server_try_cancel_thd = NULL;
+ std::thread* server_try_cancel_thd = nullptr;
auto verif = Verifier(GetParam().disable_blocking);
@@ -1331,7 +1341,7 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest {
EXPECT_EQ(verif.Next(cq_.get(), ignore_cq_result), 8);
}
- if (server_try_cancel_thd != NULL) {
+ if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
}
@@ -1405,11 +1415,15 @@ std::vector<TestScenario> CreateTestScenarios(bool test_disable_blocking,
std::vector<grpc::string> credentials_types;
std::vector<grpc::string> messages;
- credentials_types.push_back(kInsecureCredentialsType);
- auto sec_list = GetSecureCredentialsTypeList();
+ if (GetCredentialsProvider()->GetChannelCredentials(kInsecureCredentialsType,
+ nullptr) != nullptr) {
+ credentials_types.push_back(kInsecureCredentialsType);
+ }
+ auto sec_list = GetCredentialsProvider()->GetSecureCredentialsTypeList();
for (auto sec = sec_list.begin(); sec != sec_list.end(); sec++) {
credentials_types.push_back(*sec);
}
+ GPR_ASSERT(!credentials_types.empty());
messages.push_back("Hello");
for (int sz = 1; sz < test_big_limit; sz *= 2) {
diff --git a/test/cpp/end2end/client_crash_test_server.cc b/test/cpp/end2end/client_crash_test_server.cc
index 6e1457407c..0e0a105989 100644
--- a/test/cpp/end2end/client_crash_test_server.cc
+++ b/test/cpp/end2end/client_crash_test_server.cc
@@ -58,11 +58,10 @@ using namespace gflags;
namespace grpc {
namespace testing {
-class ServiceImpl GRPC_FINAL
- : public ::grpc::testing::EchoTestService::Service {
- Status BidiStream(ServerContext* context,
- ServerReaderWriter<EchoResponse, EchoRequest>* stream)
- GRPC_OVERRIDE {
+class ServiceImpl final : public ::grpc::testing::EchoTestService::Service {
+ Status BidiStream(
+ ServerContext* context,
+ ServerReaderWriter<EchoResponse, EchoRequest>* stream) override {
EchoRequest request;
EchoResponse response;
while (stream->Read(&request)) {
diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc
index b1d3ce92f6..df78557c43 100644
--- a/test/cpp/end2end/end2end_test.cc
+++ b/test/cpp/end2end/end2end_test.cc
@@ -37,6 +37,7 @@
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
+#include <grpc++/resource_quota.h>
#include <grpc++/security/auth_metadata_processor.h>
#include <grpc++/security/credentials.h>
#include <grpc++/security/server_credentials.h>
@@ -91,12 +92,12 @@ class TestMetadataCredentialsPlugin : public MetadataCredentialsPlugin {
is_blocking_(is_blocking),
is_successful_(is_successful) {}
- bool IsBlocking() const GRPC_OVERRIDE { return is_blocking_; }
+ bool IsBlocking() const override { return is_blocking_; }
- Status GetMetadata(grpc::string_ref service_url, grpc::string_ref method_name,
- const grpc::AuthContext& channel_auth_context,
- std::multimap<grpc::string, grpc::string>* metadata)
- GRPC_OVERRIDE {
+ Status GetMetadata(
+ grpc::string_ref service_url, grpc::string_ref method_name,
+ const grpc::AuthContext& channel_auth_context,
+ std::multimap<grpc::string, grpc::string>* metadata) override {
EXPECT_GT(service_url.length(), 0UL);
EXPECT_GT(method_name.length(), 0UL);
EXPECT_TRUE(channel_auth_context.IsPeerAuthenticated());
@@ -144,11 +145,11 @@ class TestAuthMetadataProcessor : public AuthMetadataProcessor {
}
// Interface implementation
- bool IsBlocking() const GRPC_OVERRIDE { return is_blocking_; }
+ bool IsBlocking() const override { return is_blocking_; }
Status Process(const InputMetadata& auth_metadata, AuthContext* context,
OutputMetadata* consumed_auth_metadata,
- OutputMetadata* response_metadata) GRPC_OVERRIDE {
+ OutputMetadata* response_metadata) override {
EXPECT_TRUE(consumed_auth_metadata != nullptr);
EXPECT_TRUE(context != nullptr);
EXPECT_TRUE(response_metadata != nullptr);
@@ -184,7 +185,7 @@ class Proxy : public ::grpc::testing::EchoTestService::Service {
: stub_(grpc::testing::EchoTestService::NewStub(channel)) {}
Status Echo(ServerContext* server_context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
std::unique_ptr<ClientContext> client_context =
ClientContext::FromServerContext(*server_context);
return stub_->Echo(client_context.get(), *request, response);
@@ -198,7 +199,7 @@ class TestServiceImplDupPkg
: public ::grpc::testing::duplicate::EchoTestService::Service {
public:
Status Echo(ServerContext* context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
response->set_message("no package");
return Status::OK;
}
@@ -208,10 +209,7 @@ class TestScenario {
public:
TestScenario(bool proxy, const grpc::string& creds_type)
: use_proxy(proxy), credentials_type(creds_type) {}
- void Log() const {
- gpr_log(GPR_INFO, "Scenario: proxy %d, credentials %s", use_proxy,
- credentials_type.c_str());
- }
+ void Log() const;
bool use_proxy;
// Although the below grpc::string is logically const, we can't declare
// them const because of a limitation in the way old compilers (e.g., gcc-4.4)
@@ -219,6 +217,19 @@ class TestScenario {
grpc::string credentials_type;
};
+static std::ostream& operator<<(std::ostream& out,
+ const TestScenario& scenario) {
+ return out << "TestScenario{use_proxy="
+ << (scenario.use_proxy ? "true" : "false") << ", credentials='"
+ << scenario.credentials_type << "'}";
+}
+
+void TestScenario::Log() const {
+ std::ostringstream out;
+ out << *this;
+ gpr_log(GPR_DEBUG, "%s", out.str().c_str());
+}
+
class End2endTest : public ::testing::TestWithParam<TestScenario> {
protected:
End2endTest()
@@ -228,7 +239,7 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
GetParam().Log();
}
- void TearDown() GRPC_OVERRIDE {
+ void TearDown() override {
if (is_server_started_) {
server_->Shutdown();
if (proxy_server_) proxy_server_->Shutdown();
@@ -240,28 +251,38 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
server_address_ << "127.0.0.1:" << port;
// Setup server
ServerBuilder builder;
- auto server_creds = GetServerCredentials(GetParam().credentials_type);
+ ConfigureServerBuilder(&builder);
+ auto server_creds = GetCredentialsProvider()->GetServerCredentials(
+ GetParam().credentials_type);
if (GetParam().credentials_type != kInsecureCredentialsType) {
server_creds->SetAuthMetadataProcessor(processor);
}
builder.AddListeningPort(server_address_.str(), server_creds);
builder.RegisterService(&service_);
builder.RegisterService("foo.test.youtube.com", &special_service_);
- builder.SetMaxMessageSize(
- kMaxMessageSize_); // For testing max message size.
builder.RegisterService(&dup_pkg_service_);
+
+ builder.SetSyncServerOption(ServerBuilder::SyncServerOption::NUM_CQS, 4);
+ builder.SetSyncServerOption(
+ ServerBuilder::SyncServerOption::CQ_TIMEOUT_MSEC, 10);
+
server_ = builder.BuildAndStart();
is_server_started_ = true;
}
+ virtual void ConfigureServerBuilder(ServerBuilder* builder) {
+ builder->SetMaxMessageSize(
+ kMaxMessageSize_); // For testing max message size.
+ }
+
void ResetChannel() {
if (!is_server_started_) {
StartServer(std::shared_ptr<AuthMetadataProcessor>());
}
EXPECT_TRUE(is_server_started_);
ChannelArguments args;
- auto channel_creds =
- GetChannelCredentials(GetParam().credentials_type, &args);
+ auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(
+ GetParam().credentials_type, &args);
if (!user_agent_prefix_.empty()) {
args.SetUserAgentPrefix(user_agent_prefix_);
}
@@ -279,6 +300,11 @@ class End2endTest : public ::testing::TestWithParam<TestScenario> {
ServerBuilder builder;
builder.AddListeningPort(proxyaddr.str(), InsecureServerCredentials());
builder.RegisterService(proxy_service_.get());
+
+ builder.SetSyncServerOption(ServerBuilder::SyncServerOption::NUM_CQS, 4);
+ builder.SetSyncServerOption(
+ ServerBuilder::SyncServerOption::CQ_TIMEOUT_MSEC, 10);
+
proxy_server_ = builder.BuildAndStart();
channel_ = CreateChannel(proxyaddr.str(), InsecureChannelCredentials());
@@ -620,7 +646,7 @@ TEST_P(End2endServerTryCancelTest, BidiStreamServerCancelAfter) {
TestBidiStreamServerCancel(CANCEL_AFTER_PROCESSING, 5);
}
-TEST_P(End2endTest, SimpleRpcWithCustomeUserAgentPrefix) {
+TEST_P(End2endTest, SimpleRpcWithCustomUserAgentPrefix) {
user_agent_prefix_ = "custom_prefix";
ResetStub();
EchoRequest request;
@@ -636,30 +662,28 @@ TEST_P(End2endTest, SimpleRpcWithCustomeUserAgentPrefix) {
auto iter = trailing_metadata.find("user-agent");
EXPECT_TRUE(iter != trailing_metadata.end());
grpc::string expected_prefix = user_agent_prefix_ + " grpc-c++/";
- EXPECT_TRUE(iter->second.starts_with(expected_prefix));
+ EXPECT_TRUE(iter->second.starts_with(expected_prefix)) << iter->second;
}
TEST_P(End2endTest, MultipleRpcsWithVariedBinaryMetadataValue) {
ResetStub();
- std::vector<std::thread*> threads;
+ std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
- threads.push_back(new std::thread(SendRpc, stub_.get(), 10, true));
+ threads.emplace_back(SendRpc, stub_.get(), 10, true);
}
for (int i = 0; i < 10; ++i) {
- threads[i]->join();
- delete threads[i];
+ threads[i].join();
}
}
TEST_P(End2endTest, MultipleRpcs) {
ResetStub();
- std::vector<std::thread*> threads;
+ std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
- threads.push_back(new std::thread(SendRpc, stub_.get(), 10, false));
+ threads.emplace_back(SendRpc, stub_.get(), 10, false);
}
for (int i = 0; i < 10; ++i) {
- threads[i]->join();
- delete threads[i];
+ threads[i].join();
}
}
@@ -877,6 +901,7 @@ TEST_P(End2endTest, RpcMaxMessageSize) {
EchoRequest request;
EchoResponse response;
request.set_message(string(kMaxMessageSize_ * 2, 'a'));
+ request.mutable_param()->set_server_die(true);
ClientContext context;
Status s = stub_->Echo(&context, request, &response);
@@ -1043,13 +1068,12 @@ TEST_P(ProxyEnd2endTest, SimpleRpcWithEmptyMessages) {
TEST_P(ProxyEnd2endTest, MultipleRpcs) {
ResetStub();
- std::vector<std::thread*> threads;
+ std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
- threads.push_back(new std::thread(SendRpc, stub_.get(), 10, false));
+ threads.emplace_back(SendRpc, stub_.get(), 10, false);
}
for (int i = 0; i < 10; ++i) {
- threads[i]->join();
- delete threads[i];
+ threads[i].join();
}
}
@@ -1280,6 +1304,7 @@ TEST_P(SecureEnd2endTest, BlockingAuthMetadataPluginAndProcessorFailure) {
EXPECT_FALSE(s.ok());
EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED);
}
+
TEST_P(SecureEnd2endTest, SetPerCallCredentials) {
ResetStub();
EchoRequest request;
@@ -1476,17 +1501,50 @@ TEST_P(SecureEnd2endTest, ClientAuthContext) {
}
}
+class ResourceQuotaEnd2endTest : public End2endTest {
+ public:
+ ResourceQuotaEnd2endTest()
+ : server_resource_quota_("server_resource_quota") {}
+
+ virtual void ConfigureServerBuilder(ServerBuilder* builder) override {
+ builder->SetResourceQuota(server_resource_quota_);
+ }
+
+ private:
+ ResourceQuota server_resource_quota_;
+};
+
+TEST_P(ResourceQuotaEnd2endTest, SimpleRequest) {
+ ResetStub();
+
+ EchoRequest request;
+ EchoResponse response;
+ request.set_message("Hello");
+
+ ClientContext context;
+ Status s = stub_->Echo(&context, request, &response);
+ EXPECT_EQ(response.message(), request.message());
+ EXPECT_TRUE(s.ok());
+}
+
std::vector<TestScenario> CreateTestScenarios(bool use_proxy,
bool test_insecure,
bool test_secure) {
std::vector<TestScenario> scenarios;
std::vector<grpc::string> credentials_types;
if (test_secure) {
- credentials_types = GetSecureCredentialsTypeList();
+ credentials_types =
+ GetCredentialsProvider()->GetSecureCredentialsTypeList();
}
if (test_insecure) {
- credentials_types.push_back(kInsecureCredentialsType);
+ // Only add insecure credentials type when it is registered with the
+ // provider. User may create providers that do not have insecure.
+ if (GetCredentialsProvider()->GetChannelCredentials(
+ kInsecureCredentialsType, nullptr) != nullptr) {
+ credentials_types.push_back(kInsecureCredentialsType);
+ }
}
+ GPR_ASSERT(!credentials_types.empty());
for (auto it = credentials_types.begin(); it != credentials_types.end();
++it) {
scenarios.emplace_back(false, *it);
@@ -1503,7 +1561,7 @@ INSTANTIATE_TEST_CASE_P(End2end, End2endTest,
INSTANTIATE_TEST_CASE_P(End2endServerTryCancel, End2endServerTryCancelTest,
::testing::ValuesIn(CreateTestScenarios(false, true,
- false)));
+ true)));
INSTANTIATE_TEST_CASE_P(ProxyEnd2end, ProxyEnd2endTest,
::testing::ValuesIn(CreateTestScenarios(true, true,
@@ -1513,6 +1571,10 @@ INSTANTIATE_TEST_CASE_P(SecureEnd2end, SecureEnd2endTest,
::testing::ValuesIn(CreateTestScenarios(false, false,
true)));
+INSTANTIATE_TEST_CASE_P(ResourceQuotaEnd2end, ResourceQuotaEnd2endTest,
+ ::testing::ValuesIn(CreateTestScenarios(false, true,
+ true)));
+
} // namespace
} // namespace testing
} // namespace grpc
diff --git a/test/cpp/end2end/filter_end2end_test.cc b/test/cpp/end2end/filter_end2end_test.cc
index 853720fd0d..bd384f68b4 100644
--- a/test/cpp/end2end/filter_end2end_test.cc
+++ b/test/cpp/end2end/filter_end2end_test.cc
@@ -78,35 +78,35 @@ namespace {
int global_num_connections = 0;
int global_num_calls = 0;
-mutex global_mu;
+std::mutex global_mu;
void IncrementConnectionCounter() {
- unique_lock<mutex> lock(global_mu);
+ std::unique_lock<std::mutex> lock(global_mu);
++global_num_connections;
}
void ResetConnectionCounter() {
- unique_lock<mutex> lock(global_mu);
+ std::unique_lock<std::mutex> lock(global_mu);
global_num_connections = 0;
}
int GetConnectionCounterValue() {
- unique_lock<mutex> lock(global_mu);
+ std::unique_lock<std::mutex> lock(global_mu);
return global_num_connections;
}
void IncrementCallCounter() {
- unique_lock<mutex> lock(global_mu);
+ std::unique_lock<std::mutex> lock(global_mu);
++global_num_calls;
}
void ResetCallCounter() {
- unique_lock<mutex> lock(global_mu);
+ std::unique_lock<std::mutex> lock(global_mu);
global_num_calls = 0;
}
int GetCallCounterValue() {
- unique_lock<mutex> lock(global_mu);
+ std::unique_lock<std::mutex> lock(global_mu);
return global_num_calls;
}
@@ -114,20 +114,17 @@ int GetCallCounterValue() {
class ChannelDataImpl : public ChannelData {
public:
- ChannelDataImpl(const grpc_channel_args& args, const char* peer)
- : ChannelData(args, peer) {
+ grpc_error* Init(grpc_exec_ctx* exec_ctx, grpc_channel_element_args* args) {
IncrementConnectionCounter();
+ return GRPC_ERROR_NONE;
}
};
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
+ TransportStreamOp* op) override {
+ // Incrementing the counter could be done from Init(), 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());
@@ -138,7 +135,7 @@ class FilterEnd2endTest : public ::testing::Test {
protected:
FilterEnd2endTest() : server_host_("localhost") {}
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
int port = grpc_pick_unused_port_or_die();
server_address_ << server_host_ << ":" << port;
// Setup server
@@ -150,7 +147,7 @@ class FilterEnd2endTest : public ::testing::Test {
server_ = builder.BuildAndStart();
}
- void TearDown() GRPC_OVERRIDE {
+ void TearDown() override {
server_->Shutdown();
void* ignored_tag;
bool ignored_ok;
diff --git a/test/cpp/end2end/generic_end2end_test.cc b/test/cpp/end2end/generic_end2end_test.cc
index 57efa5fa17..25c221bb2b 100644
--- a/test/cpp/end2end/generic_end2end_test.cc
+++ b/test/cpp/end2end/generic_end2end_test.cc
@@ -75,7 +75,7 @@ class GenericEnd2endTest : public ::testing::Test {
protected:
GenericEnd2endTest() : server_host_("localhost") {}
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
int port = grpc_pick_unused_port_or_die();
server_address_ << server_host_ << ":" << port;
// Setup server
@@ -91,7 +91,7 @@ class GenericEnd2endTest : public ::testing::Test {
server_ = builder.BuildAndStart();
}
- void TearDown() GRPC_OVERRIDE {
+ void TearDown() override {
server_->Shutdown();
void* ignored_tag;
bool ignored_ok;
diff --git a/test/cpp/end2end/health_service_end2end_test.cc b/test/cpp/end2end/health_service_end2end_test.cc
new file mode 100644
index 0000000000..3d51007857
--- /dev/null
+++ b/test/cpp/end2end/health_service_end2end_test.cc
@@ -0,0 +1,323 @@
+/*
+ *
+ * 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 <thread>
+#include <vector>
+
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/ext/health_check_service_server_builder_option.h>
+#include <grpc++/health_check_service_interface.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc++/server_context.h>
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <gtest/gtest.h>
+
+#include "src/proto/grpc/health/v1/health.grpc.pb.h"
+#include "src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.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/end2end/test_service_impl.h"
+
+using grpc::health::v1::Health;
+using grpc::health::v1::HealthCheckRequest;
+using grpc::health::v1::HealthCheckResponse;
+
+namespace grpc {
+namespace testing {
+namespace {
+
+// A sample sync implementation of the health checking service. This does the
+// same thing as the default one.
+class HealthCheckServiceImpl : public ::grpc::health::v1::Health::Service {
+ public:
+ Status Check(ServerContext* context, const HealthCheckRequest* request,
+ HealthCheckResponse* response) override {
+ std::lock_guard<std::mutex> lock(mu_);
+ auto iter = status_map_.find(request->service());
+ if (iter == status_map_.end()) {
+ return Status(StatusCode::NOT_FOUND, "");
+ }
+ response->set_status(iter->second);
+ return Status::OK;
+ }
+
+ void SetStatus(const grpc::string& service_name,
+ HealthCheckResponse::ServingStatus status) {
+ std::lock_guard<std::mutex> lock(mu_);
+ status_map_[service_name] = status;
+ }
+
+ void SetAll(HealthCheckResponse::ServingStatus status) {
+ std::lock_guard<std::mutex> lock(mu_);
+ for (auto iter = status_map_.begin(); iter != status_map_.end(); ++iter) {
+ iter->second = status;
+ }
+ }
+
+ private:
+ std::mutex mu_;
+ std::map<const grpc::string, HealthCheckResponse::ServingStatus> status_map_;
+};
+
+// A custom implementation of the health checking service interface. This is
+// used to test that it prevents the server from creating a default service and
+// also serves as an example of how to override the default service.
+class CustomHealthCheckService : public HealthCheckServiceInterface {
+ public:
+ explicit CustomHealthCheckService(HealthCheckServiceImpl* impl)
+ : impl_(impl) {
+ impl_->SetStatus("", HealthCheckResponse::SERVING);
+ }
+ void SetServingStatus(const grpc::string& service_name,
+ bool serving) override {
+ impl_->SetStatus(service_name, serving ? HealthCheckResponse::SERVING
+ : HealthCheckResponse::NOT_SERVING);
+ }
+
+ void SetServingStatus(bool serving) override {
+ impl_->SetAll(serving ? HealthCheckResponse::SERVING
+ : HealthCheckResponse::NOT_SERVING);
+ }
+
+ private:
+ HealthCheckServiceImpl* impl_; // not owned
+};
+
+void LoopCompletionQueue(ServerCompletionQueue* cq) {
+ void* tag;
+ bool ok;
+ while (cq->Next(&tag, &ok)) {
+ abort(); // Nothing should come out of the cq.
+ }
+}
+
+class HealthServiceEnd2endTest : public ::testing::Test {
+ protected:
+ HealthServiceEnd2endTest() {}
+
+ void SetUpServer(bool register_sync_test_service, bool add_async_cq,
+ bool explicit_health_service,
+ std::unique_ptr<HealthCheckServiceInterface> service) {
+ int port = grpc_pick_unused_port_or_die();
+ server_address_ << "localhost:" << port;
+
+ bool register_sync_health_service_impl =
+ explicit_health_service && service != nullptr;
+
+ // Setup server
+ ServerBuilder builder;
+ if (explicit_health_service) {
+ std::unique_ptr<ServerBuilderOption> option(
+ new HealthCheckServiceServerBuilderOption(std::move(service)));
+ builder.SetOption(std::move(option));
+ }
+ builder.AddListeningPort(server_address_.str(),
+ grpc::InsecureServerCredentials());
+ if (register_sync_test_service) {
+ // Register a sync service.
+ builder.RegisterService(&echo_test_service_);
+ }
+ if (register_sync_health_service_impl) {
+ builder.RegisterService(&health_check_service_impl_);
+ }
+ if (add_async_cq) {
+ cq_ = builder.AddCompletionQueue();
+ }
+ server_ = builder.BuildAndStart();
+ }
+
+ void TearDown() override {
+ if (server_) {
+ server_->Shutdown();
+ if (cq_ != nullptr) {
+ cq_->Shutdown();
+ }
+ if (cq_thread_.joinable()) {
+ cq_thread_.join();
+ }
+ }
+ }
+
+ void ResetStubs() {
+ std::shared_ptr<Channel> channel =
+ CreateChannel(server_address_.str(), InsecureChannelCredentials());
+ hc_stub_ = grpc::health::v1::Health::NewStub(channel);
+ }
+
+ // When the expected_status is NOT OK, we do not care about the response.
+ void SendHealthCheckRpc(const grpc::string& service_name,
+ const Status& expected_status) {
+ EXPECT_FALSE(expected_status.ok());
+ SendHealthCheckRpc(service_name, expected_status,
+ HealthCheckResponse::UNKNOWN);
+ }
+
+ void SendHealthCheckRpc(
+ const grpc::string& service_name, const Status& expected_status,
+ HealthCheckResponse::ServingStatus expected_serving_status) {
+ HealthCheckRequest request;
+ request.set_service(service_name);
+ HealthCheckResponse response;
+ ClientContext context;
+ Status s = hc_stub_->Check(&context, request, &response);
+ EXPECT_EQ(expected_status.error_code(), s.error_code());
+ if (s.ok()) {
+ EXPECT_EQ(expected_serving_status, response.status());
+ }
+ }
+
+ void VerifyHealthCheckService() {
+ HealthCheckServiceInterface* service = server_->GetHealthCheckService();
+ EXPECT_TRUE(service != nullptr);
+ const grpc::string kHealthyService("healthy_service");
+ const grpc::string kUnhealthyService("unhealthy_service");
+ const grpc::string kNotRegisteredService("not_registered");
+ service->SetServingStatus(kHealthyService, true);
+ service->SetServingStatus(kUnhealthyService, false);
+
+ ResetStubs();
+
+ SendHealthCheckRpc("", Status::OK, HealthCheckResponse::SERVING);
+ SendHealthCheckRpc(kHealthyService, Status::OK,
+ HealthCheckResponse::SERVING);
+ SendHealthCheckRpc(kUnhealthyService, Status::OK,
+ HealthCheckResponse::NOT_SERVING);
+ SendHealthCheckRpc(kNotRegisteredService,
+ Status(StatusCode::NOT_FOUND, ""));
+
+ service->SetServingStatus(false);
+ SendHealthCheckRpc("", Status::OK, HealthCheckResponse::NOT_SERVING);
+ SendHealthCheckRpc(kHealthyService, Status::OK,
+ HealthCheckResponse::NOT_SERVING);
+ SendHealthCheckRpc(kUnhealthyService, Status::OK,
+ HealthCheckResponse::NOT_SERVING);
+ SendHealthCheckRpc(kNotRegisteredService,
+ Status(StatusCode::NOT_FOUND, ""));
+ }
+
+ TestServiceImpl echo_test_service_;
+ HealthCheckServiceImpl health_check_service_impl_;
+ std::unique_ptr<Health::Stub> hc_stub_;
+ std::unique_ptr<ServerCompletionQueue> cq_;
+ std::unique_ptr<Server> server_;
+ std::ostringstream server_address_;
+ std::thread cq_thread_;
+};
+
+TEST_F(HealthServiceEnd2endTest, DefaultHealthServiceDisabled) {
+ EnableDefaultHealthCheckService(false);
+ EXPECT_FALSE(DefaultHealthCheckServiceEnabled());
+ SetUpServer(true, false, false, nullptr);
+ HealthCheckServiceInterface* default_service =
+ server_->GetHealthCheckService();
+ EXPECT_TRUE(default_service == nullptr);
+
+ ResetStubs();
+
+ SendHealthCheckRpc("", Status(StatusCode::UNIMPLEMENTED, ""));
+}
+
+TEST_F(HealthServiceEnd2endTest, DefaultHealthService) {
+ EnableDefaultHealthCheckService(true);
+ EXPECT_TRUE(DefaultHealthCheckServiceEnabled());
+ SetUpServer(true, false, false, nullptr);
+ VerifyHealthCheckService();
+
+ // The default service has a size limit of the service name.
+ const grpc::string kTooLongServiceName(201, 'x');
+ SendHealthCheckRpc(kTooLongServiceName,
+ Status(StatusCode::INVALID_ARGUMENT, ""));
+}
+
+// The server has no sync service.
+TEST_F(HealthServiceEnd2endTest, DefaultHealthServiceAsyncOnly) {
+ EnableDefaultHealthCheckService(true);
+ EXPECT_TRUE(DefaultHealthCheckServiceEnabled());
+ SetUpServer(false, true, false, nullptr);
+ cq_thread_ = std::thread(LoopCompletionQueue, cq_.get());
+
+ HealthCheckServiceInterface* default_service =
+ server_->GetHealthCheckService();
+ EXPECT_TRUE(default_service == nullptr);
+
+ ResetStubs();
+
+ SendHealthCheckRpc("", Status(StatusCode::UNIMPLEMENTED, ""));
+}
+
+// Provide an empty service to disable the default service.
+TEST_F(HealthServiceEnd2endTest, ExplicitlyDisableViaOverride) {
+ EnableDefaultHealthCheckService(true);
+ EXPECT_TRUE(DefaultHealthCheckServiceEnabled());
+ std::unique_ptr<HealthCheckServiceInterface> empty_service;
+ SetUpServer(true, false, true, std::move(empty_service));
+ HealthCheckServiceInterface* service = server_->GetHealthCheckService();
+ EXPECT_TRUE(service == nullptr);
+
+ ResetStubs();
+
+ SendHealthCheckRpc("", Status(StatusCode::UNIMPLEMENTED, ""));
+}
+
+// Provide an explicit override of health checking service interface.
+TEST_F(HealthServiceEnd2endTest, ExplicitlyOverride) {
+ EnableDefaultHealthCheckService(true);
+ EXPECT_TRUE(DefaultHealthCheckServiceEnabled());
+ std::unique_ptr<HealthCheckServiceInterface> override_service(
+ new CustomHealthCheckService(&health_check_service_impl_));
+ HealthCheckServiceInterface* underlying_service = override_service.get();
+ SetUpServer(false, false, true, std::move(override_service));
+ HealthCheckServiceInterface* service = server_->GetHealthCheckService();
+ EXPECT_TRUE(service == underlying_service);
+
+ ResetStubs();
+
+ VerifyHealthCheckService();
+}
+
+} // namespace
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/end2end/hybrid_end2end_test.cc b/test/cpp/end2end/hybrid_end2end_test.cc
index 8cd2e66347..a4ba76fed1 100644
--- a/test/cpp/end2end/hybrid_end2end_test.cc
+++ b/test/cpp/end2end/hybrid_end2end_test.cc
@@ -188,7 +188,7 @@ class TestServiceImplDupPkg
: public ::grpc::testing::duplicate::EchoTestService::Service {
public:
Status Echo(ServerContext* context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
response->set_message(request->message() + "_dup");
return Status::OK;
}
@@ -230,7 +230,7 @@ class HybridEnd2endTest : public ::testing::Test {
server_ = builder.BuildAndStart();
}
- void TearDown() GRPC_OVERRIDE {
+ void TearDown() override {
if (server_) {
server_->Shutdown();
}
@@ -320,6 +320,29 @@ class HybridEnd2endTest : public ::testing::Test {
EXPECT_TRUE(s.ok());
}
+ void SendSimpleServerStreamingToDupService() {
+ std::shared_ptr<Channel> channel =
+ CreateChannel(server_address_.str(), InsecureChannelCredentials());
+ auto stub = grpc::testing::duplicate::EchoTestService::NewStub(channel);
+ EchoRequest request;
+ EchoResponse response;
+ ClientContext context;
+ context.set_wait_for_ready(true);
+ request.set_message("hello");
+
+ auto stream = stub->ResponseStream(&context, request);
+ EXPECT_TRUE(stream->Read(&response));
+ EXPECT_EQ(response.message(), request.message() + "0_dup");
+ EXPECT_TRUE(stream->Read(&response));
+ EXPECT_EQ(response.message(), request.message() + "1_dup");
+ EXPECT_TRUE(stream->Read(&response));
+ EXPECT_EQ(response.message(), request.message() + "2_dup");
+ EXPECT_FALSE(stream->Read(&response));
+
+ Status s = stream->Finish();
+ EXPECT_TRUE(s.ok());
+ }
+
void SendBidiStreaming() {
EchoRequest request;
EchoResponse response;
@@ -426,9 +449,9 @@ class StreamedUnaryDupPkg
: public duplicate::EchoTestService::WithStreamedUnaryMethod_Echo<
TestServiceImplDupPkg> {
public:
- Status StreamedEcho(ServerContext* context,
- ServerUnaryStreamer<EchoRequest, EchoResponse>* stream)
- GRPC_OVERRIDE {
+ Status StreamedEcho(
+ ServerContext* context,
+ ServerUnaryStreamer<EchoRequest, EchoResponse>* stream) override {
EchoRequest req;
EchoResponse resp;
uint32_t next_msg_sz;
@@ -464,9 +487,9 @@ TEST_F(HybridEnd2endTest,
class FullyStreamedUnaryDupPkg
: public duplicate::EchoTestService::StreamedUnaryService {
public:
- Status StreamedEcho(ServerContext* context,
- ServerUnaryStreamer<EchoRequest, EchoResponse>* stream)
- GRPC_OVERRIDE {
+ Status StreamedEcho(
+ ServerContext* context,
+ ServerUnaryStreamer<EchoRequest, EchoResponse>* stream) override {
EchoRequest req;
EchoResponse resp;
uint32_t next_msg_sz;
@@ -498,6 +521,140 @@ TEST_F(HybridEnd2endTest,
request_stream_handler_thread.join();
}
+// Add a second service with one sync split server streaming method.
+class SplitResponseStreamDupPkg
+ : public duplicate::EchoTestService::
+ WithSplitStreamingMethod_ResponseStream<TestServiceImplDupPkg> {
+ public:
+ Status StreamedResponseStream(
+ ServerContext* context,
+ ServerSplitStreamer<EchoRequest, EchoResponse>* stream) override {
+ EchoRequest req;
+ EchoResponse resp;
+ uint32_t next_msg_sz;
+ stream->NextMessageSize(&next_msg_sz);
+ gpr_log(GPR_INFO, "Split Streamed Next Message Size is %u", next_msg_sz);
+ GPR_ASSERT(stream->Read(&req));
+ for (int i = 0; i < kNumResponseStreamsMsgs; i++) {
+ resp.set_message(req.message() + grpc::to_string(i) + "_dup");
+ GPR_ASSERT(stream->Write(resp));
+ }
+ return Status::OK;
+ }
+};
+
+TEST_F(HybridEnd2endTest,
+ AsyncRequestStreamResponseStream_SyncSplitStreamedDupService) {
+ typedef EchoTestService::WithAsyncMethod_RequestStream<
+ EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>>
+ SType;
+ SType service;
+ SplitResponseStreamDupPkg dup_service;
+ SetUpServer(&service, &dup_service, nullptr, 8192);
+ ResetStub();
+ std::thread response_stream_handler_thread(HandleServerStreaming<SType>,
+ &service, cqs_[0].get());
+ std::thread request_stream_handler_thread(HandleClientStreaming<SType>,
+ &service, cqs_[1].get());
+ TestAllMethods();
+ SendSimpleServerStreamingToDupService();
+ response_stream_handler_thread.join();
+ request_stream_handler_thread.join();
+}
+
+// Add a second service that is fully split server streamed
+class FullySplitStreamedDupPkg
+ : public duplicate::EchoTestService::SplitStreamedService {
+ public:
+ Status StreamedResponseStream(
+ ServerContext* context,
+ ServerSplitStreamer<EchoRequest, EchoResponse>* stream) override {
+ EchoRequest req;
+ EchoResponse resp;
+ uint32_t next_msg_sz;
+ stream->NextMessageSize(&next_msg_sz);
+ gpr_log(GPR_INFO, "Split Streamed Next Message Size is %u", next_msg_sz);
+ GPR_ASSERT(stream->Read(&req));
+ for (int i = 0; i < kNumResponseStreamsMsgs; i++) {
+ resp.set_message(req.message() + grpc::to_string(i) + "_dup");
+ GPR_ASSERT(stream->Write(resp));
+ }
+ return Status::OK;
+ }
+};
+
+TEST_F(HybridEnd2endTest,
+ AsyncRequestStreamResponseStream_FullySplitStreamedDupService) {
+ typedef EchoTestService::WithAsyncMethod_RequestStream<
+ EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>>
+ SType;
+ SType service;
+ FullySplitStreamedDupPkg dup_service;
+ SetUpServer(&service, &dup_service, nullptr, 8192);
+ ResetStub();
+ std::thread response_stream_handler_thread(HandleServerStreaming<SType>,
+ &service, cqs_[0].get());
+ std::thread request_stream_handler_thread(HandleClientStreaming<SType>,
+ &service, cqs_[1].get());
+ TestAllMethods();
+ SendSimpleServerStreamingToDupService();
+ response_stream_handler_thread.join();
+ request_stream_handler_thread.join();
+}
+
+// Add a second service that is fully server streamed
+class FullyStreamedDupPkg : public duplicate::EchoTestService::StreamedService {
+ public:
+ Status StreamedEcho(
+ ServerContext* context,
+ ServerUnaryStreamer<EchoRequest, EchoResponse>* stream) override {
+ EchoRequest req;
+ EchoResponse resp;
+ uint32_t next_msg_sz;
+ stream->NextMessageSize(&next_msg_sz);
+ gpr_log(GPR_INFO, "Streamed Unary Next Message Size is %u", next_msg_sz);
+ GPR_ASSERT(stream->Read(&req));
+ resp.set_message(req.message() + "_dup");
+ GPR_ASSERT(stream->Write(resp));
+ return Status::OK;
+ }
+ Status StreamedResponseStream(
+ ServerContext* context,
+ ServerSplitStreamer<EchoRequest, EchoResponse>* stream) override {
+ EchoRequest req;
+ EchoResponse resp;
+ uint32_t next_msg_sz;
+ stream->NextMessageSize(&next_msg_sz);
+ gpr_log(GPR_INFO, "Split Streamed Next Message Size is %u", next_msg_sz);
+ GPR_ASSERT(stream->Read(&req));
+ for (int i = 0; i < kNumResponseStreamsMsgs; i++) {
+ resp.set_message(req.message() + grpc::to_string(i) + "_dup");
+ GPR_ASSERT(stream->Write(resp));
+ }
+ return Status::OK;
+ }
+};
+
+TEST_F(HybridEnd2endTest,
+ AsyncRequestStreamResponseStream_FullyStreamedDupService) {
+ typedef EchoTestService::WithAsyncMethod_RequestStream<
+ EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>>
+ SType;
+ SType service;
+ FullyStreamedDupPkg dup_service;
+ SetUpServer(&service, &dup_service, nullptr, 8192);
+ ResetStub();
+ std::thread response_stream_handler_thread(HandleServerStreaming<SType>,
+ &service, cqs_[0].get());
+ std::thread request_stream_handler_thread(HandleClientStreaming<SType>,
+ &service, cqs_[1].get());
+ TestAllMethods();
+ SendEchoToDupService();
+ SendSimpleServerStreamingToDupService();
+ response_stream_handler_thread.join();
+ request_stream_handler_thread.join();
+}
+
// Add a second service with one async method.
TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream_AsyncDupService) {
typedef EchoTestService::WithAsyncMethod_RequestStream<
diff --git a/test/cpp/end2end/mock_test.cc b/test/cpp/end2end/mock_test.cc
index 0da5861b67..d6664da5a0 100644
--- a/test/cpp/end2end/mock_test.cc
+++ b/test/cpp/end2end/mock_test.cc
@@ -61,46 +61,44 @@ namespace testing {
namespace {
template <class W, class R>
-class MockClientReaderWriter GRPC_FINAL
- : public ClientReaderWriterInterface<W, R> {
+class MockClientReaderWriter final : public ClientReaderWriterInterface<W, R> {
public:
- void WaitForInitialMetadata() GRPC_OVERRIDE {}
- bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
+ void WaitForInitialMetadata() override {}
+ bool NextMessageSize(uint32_t* sz) override {
*sz = UINT_MAX;
return true;
}
- bool Read(R* msg) GRPC_OVERRIDE { return true; }
- bool Write(const W& msg) GRPC_OVERRIDE { return true; }
- bool WritesDone() GRPC_OVERRIDE { return true; }
- Status Finish() GRPC_OVERRIDE { return Status::OK; }
+ bool Read(R* msg) override { return true; }
+ bool Write(const W& msg) override { return true; }
+ bool WritesDone() override { return true; }
+ Status Finish() override { return Status::OK; }
};
template <>
-class MockClientReaderWriter<EchoRequest, EchoResponse> GRPC_FINAL
+class MockClientReaderWriter<EchoRequest, EchoResponse> final
: public ClientReaderWriterInterface<EchoRequest, EchoResponse> {
public:
MockClientReaderWriter() : writes_done_(false) {}
- void WaitForInitialMetadata() GRPC_OVERRIDE {}
- bool NextMessageSize(uint32_t* sz) GRPC_OVERRIDE {
+ void WaitForInitialMetadata() override {}
+ bool NextMessageSize(uint32_t* sz) override {
*sz = UINT_MAX;
return true;
}
- bool Read(EchoResponse* msg) GRPC_OVERRIDE {
+ bool Read(EchoResponse* msg) override {
if (writes_done_) return false;
msg->set_message(last_message_);
return true;
}
- bool Write(const EchoRequest& msg,
- const WriteOptions& options) GRPC_OVERRIDE {
+ bool Write(const EchoRequest& msg, const WriteOptions& options) override {
gpr_log(GPR_INFO, "mock recv msg %s", msg.message().c_str());
last_message_ = msg.message();
return true;
}
- bool WritesDone() GRPC_OVERRIDE {
+ bool WritesDone() override {
writes_done_ = true;
return true;
}
- Status Finish() GRPC_OVERRIDE { return Status::OK; }
+ Status Finish() override { return Status::OK; }
private:
bool writes_done_;
@@ -113,51 +111,51 @@ class MockStub : public EchoTestService::StubInterface {
MockStub() {}
~MockStub() {}
Status Echo(ClientContext* context, const EchoRequest& request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
response->set_message(request.message());
return Status::OK;
}
Status Unimplemented(ClientContext* context, const EchoRequest& request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
return Status::OK;
}
private:
ClientAsyncResponseReaderInterface<EchoResponse>* AsyncEchoRaw(
ClientContext* context, const EchoRequest& request,
- CompletionQueue* cq) GRPC_OVERRIDE {
+ CompletionQueue* cq) override {
return nullptr;
}
ClientWriterInterface<EchoRequest>* RequestStreamRaw(
- ClientContext* context, EchoResponse* response) GRPC_OVERRIDE {
+ ClientContext* context, EchoResponse* response) override {
return nullptr;
}
ClientAsyncWriterInterface<EchoRequest>* AsyncRequestStreamRaw(
ClientContext* context, EchoResponse* response, CompletionQueue* cq,
- void* tag) GRPC_OVERRIDE {
+ void* tag) override {
return nullptr;
}
ClientReaderInterface<EchoResponse>* ResponseStreamRaw(
- ClientContext* context, const EchoRequest& request) GRPC_OVERRIDE {
+ ClientContext* context, const EchoRequest& request) override {
return nullptr;
}
ClientAsyncReaderInterface<EchoResponse>* AsyncResponseStreamRaw(
ClientContext* context, const EchoRequest& request, CompletionQueue* cq,
- void* tag) GRPC_OVERRIDE {
+ void* tag) override {
return nullptr;
}
ClientReaderWriterInterface<EchoRequest, EchoResponse>* BidiStreamRaw(
- ClientContext* context) GRPC_OVERRIDE {
+ ClientContext* context) override {
return new MockClientReaderWriter<EchoRequest, EchoResponse>();
}
ClientAsyncReaderWriterInterface<EchoRequest, EchoResponse>*
AsyncBidiStreamRaw(ClientContext* context, CompletionQueue* cq,
- void* tag) GRPC_OVERRIDE {
+ void* tag) override {
return nullptr;
}
ClientAsyncResponseReaderInterface<EchoResponse>* AsyncUnimplementedRaw(
ClientContext* context, const EchoRequest& request,
- CompletionQueue* cq) GRPC_OVERRIDE {
+ CompletionQueue* cq) override {
return nullptr;
}
};
@@ -216,14 +214,14 @@ class FakeClient {
class TestServiceImpl : public EchoTestService::Service {
public:
Status Echo(ServerContext* context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
response->set_message(request->message());
return Status::OK;
}
- Status BidiStream(ServerContext* context,
- ServerReaderWriter<EchoResponse, EchoRequest>* stream)
- GRPC_OVERRIDE {
+ Status BidiStream(
+ ServerContext* context,
+ ServerReaderWriter<EchoResponse, EchoRequest>* stream) override {
EchoRequest request;
EchoResponse response;
while (stream->Read(&request)) {
@@ -239,7 +237,7 @@ class MockTest : public ::testing::Test {
protected:
MockTest() {}
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
int port = grpc_pick_unused_port_or_die();
server_address_ << "localhost:" << port;
// Setup server
@@ -250,7 +248,7 @@ class MockTest : public ::testing::Test {
server_ = builder.BuildAndStart();
}
- void TearDown() GRPC_OVERRIDE { server_->Shutdown(); }
+ void TearDown() override { server_->Shutdown(); }
void ResetStub() {
std::shared_ptr<Channel> channel =
diff --git a/test/cpp/end2end/proto_server_reflection_test.cc b/test/cpp/end2end/proto_server_reflection_test.cc
index 75efd01f06..8b9688d200 100644
--- a/test/cpp/end2end/proto_server_reflection_test.cc
+++ b/test/cpp/end2end/proto_server_reflection_test.cc
@@ -56,7 +56,7 @@ class ProtoServerReflectionTest : public ::testing::Test {
public:
ProtoServerReflectionTest() {}
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
port_ = grpc_pick_unused_port_or_die();
ref_desc_pool_ = protobuf::DescriptorPool::generated_pool();
diff --git a/test/cpp/end2end/round_robin_end2end_test.cc b/test/cpp/end2end/round_robin_end2end_test.cc
new file mode 100644
index 0000000000..cc340b96b3
--- /dev/null
+++ b/test/cpp/end2end/round_robin_end2end_test.cc
@@ -0,0 +1,216 @@
+/*
+ *
+ * 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 <thread>
+
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc/grpc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/thd.h>
+#include <grpc/support/time.h>
+#include <gtest/gtest.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/end2end/test_service_impl.h"
+
+using grpc::testing::EchoRequest;
+using grpc::testing::EchoResponse;
+using std::chrono::system_clock;
+
+namespace grpc {
+namespace testing {
+namespace {
+
+// Subclass of TestServiceImpl that increments a request counter for
+// every call to the Echo RPC.
+class MyTestServiceImpl : public TestServiceImpl {
+ public:
+ MyTestServiceImpl() : request_count_(0) {}
+
+ Status Echo(ServerContext* context, const EchoRequest* request,
+ EchoResponse* response) override {
+ {
+ std::unique_lock<std::mutex> lock(mu_);
+ ++request_count_;
+ }
+ return TestServiceImpl::Echo(context, request, response);
+ }
+
+ int request_count() {
+ std::unique_lock<std::mutex> lock(mu_);
+ return request_count_;
+ }
+
+ private:
+ std::mutex mu_;
+ int request_count_;
+};
+
+class RoundRobinEnd2endTest : public ::testing::Test {
+ protected:
+ RoundRobinEnd2endTest() : server_host_("localhost") {}
+
+ void StartServers(int num_servers) {
+ for (int i = 0; i < num_servers; ++i) {
+ servers_.emplace_back(new ServerData(server_host_));
+ }
+ }
+
+ void TearDown() override {
+ for (size_t i = 0; i < servers_.size(); ++i) {
+ servers_[i]->Shutdown();
+ }
+ }
+
+ void ResetStub(bool round_robin) {
+ ChannelArguments args;
+ if (round_robin) args.SetLoadBalancingPolicyName("round_robin");
+ std::ostringstream uri;
+ uri << "ipv4:///";
+ for (size_t i = 0; i < servers_.size() - 1; ++i) {
+ uri << "127.0.0.1:" << servers_[i]->port_ << ",";
+ }
+ uri << "127.0.0.1:" << servers_[servers_.size() - 1]->port_;
+ channel_ =
+ CreateCustomChannel(uri.str(), InsecureChannelCredentials(), args);
+ stub_ = grpc::testing::EchoTestService::NewStub(channel_);
+ }
+
+ void SendRpc(int num_rpcs) {
+ EchoRequest request;
+ EchoResponse response;
+ request.set_message("Live long and prosper.");
+ for (int i = 0; i < num_rpcs; i++) {
+ ClientContext context;
+ Status status = stub_->Echo(&context, request, &response);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(response.message(), request.message());
+ }
+ }
+
+ struct ServerData {
+ int port_;
+ std::unique_ptr<Server> server_;
+ MyTestServiceImpl service_;
+ std::unique_ptr<std::thread> thread_;
+
+ explicit ServerData(const grpc::string& server_host) {
+ port_ = grpc_pick_unused_port_or_die();
+ gpr_log(GPR_INFO, "starting server on port %d", port_);
+ std::mutex mu;
+ std::condition_variable cond;
+ thread_.reset(new std::thread(
+ std::bind(&ServerData::Start, this, server_host, &mu, &cond)));
+ std::unique_lock<std::mutex> lock(mu);
+ cond.wait(lock);
+ gpr_log(GPR_INFO, "server startup complete");
+ }
+
+ void Start(const grpc::string& server_host, std::mutex* mu,
+ std::condition_variable* cond) {
+ std::ostringstream server_address;
+ server_address << server_host << ":" << port_;
+ ServerBuilder builder;
+ builder.AddListeningPort(server_address.str(),
+ InsecureServerCredentials());
+ builder.RegisterService(&service_);
+ server_ = builder.BuildAndStart();
+ std::lock_guard<std::mutex> lock(*mu);
+ cond->notify_one();
+ }
+
+ void Shutdown() {
+ server_->Shutdown();
+ thread_->join();
+ }
+ };
+
+ const grpc::string server_host_;
+ CompletionQueue cli_cq_;
+ std::shared_ptr<Channel> channel_;
+ std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
+ std::vector<std::unique_ptr<ServerData>> servers_;
+};
+
+TEST_F(RoundRobinEnd2endTest, PickFirst) {
+ // Start servers and send one RPC per server.
+ const int kNumServers = 3;
+ StartServers(kNumServers);
+ ResetStub(false /* round_robin */);
+ SendRpc(kNumServers);
+ // All requests should have gone to a single server.
+ bool found = false;
+ for (size_t i = 0; i < servers_.size(); ++i) {
+ const int request_count = servers_[i]->service_.request_count();
+ if (request_count == kNumServers) {
+ found = true;
+ } else {
+ EXPECT_EQ(0, request_count);
+ }
+ }
+ EXPECT_TRUE(found);
+ // Check LB policy name for the channel.
+ EXPECT_EQ("pick_first", channel_->GetLoadBalancingPolicyName());
+}
+
+TEST_F(RoundRobinEnd2endTest, RoundRobin) {
+ // Start servers and send one RPC per server.
+ const int kNumServers = 3;
+ StartServers(kNumServers);
+ ResetStub(true /* round_robin */);
+ SendRpc(kNumServers);
+ // One request should have gone to each server.
+ for (size_t i = 0; i < servers_.size(); ++i) {
+ EXPECT_EQ(1, servers_[i]->service_.request_count());
+ }
+ // Check LB policy name for the channel.
+ EXPECT_EQ("round_robin", channel_->GetLoadBalancingPolicyName());
+}
+
+} // namespace
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/end2end/server_builder_plugin_test.cc b/test/cpp/end2end/server_builder_plugin_test.cc
index b967a5d1e9..1b6f4ce37d 100644
--- a/test/cpp/end2end/server_builder_plugin_test.cc
+++ b/test/cpp/end2end/server_builder_plugin_test.cc
@@ -31,13 +31,14 @@
*
*/
+#include <thread>
+
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
#include <grpc++/create_channel.h>
#include <grpc++/impl/server_builder_option.h>
#include <grpc++/impl/server_builder_plugin.h>
#include <grpc++/impl/server_initializer.h>
-#include <grpc++/impl/thd.h>
#include <grpc++/security/credentials.h>
#include <grpc++/security/server_credentials.h>
#include <grpc++/server.h>
@@ -65,29 +66,29 @@ class TestServerBuilderPlugin : public ServerBuilderPlugin {
register_service_ = false;
}
- grpc::string name() GRPC_OVERRIDE { return PLUGIN_NAME; }
+ grpc::string name() override { return PLUGIN_NAME; }
- void InitServer(ServerInitializer* si) GRPC_OVERRIDE {
+ void InitServer(ServerInitializer* si) override {
init_server_is_called_ = true;
if (register_service_) {
si->RegisterService(service_);
}
}
- void Finish(ServerInitializer* si) GRPC_OVERRIDE { finish_is_called_ = true; }
+ void Finish(ServerInitializer* si) override { finish_is_called_ = true; }
- void ChangeArguments(const grpc::string& name, void* value) GRPC_OVERRIDE {
+ void ChangeArguments(const grpc::string& name, void* value) override {
change_arguments_is_called_ = true;
}
- bool has_async_methods() const GRPC_OVERRIDE {
+ bool has_async_methods() const override {
if (register_service_) {
return service_->has_async_methods();
}
return false;
}
- bool has_sync_methods() const GRPC_OVERRIDE {
+ bool has_sync_methods() const override {
if (register_service_) {
return service_->has_synchronous_methods();
}
@@ -112,10 +113,10 @@ class InsertPluginServerBuilderOption : public ServerBuilderOption {
public:
InsertPluginServerBuilderOption() { register_service_ = false; }
- void UpdateArguments(ChannelArguments* arg) GRPC_OVERRIDE {}
+ void UpdateArguments(ChannelArguments* arg) override {}
- void UpdatePlugins(std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins)
- GRPC_OVERRIDE {
+ void UpdatePlugins(
+ std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) override {
plugins->clear();
std::unique_ptr<TestServerBuilderPlugin> plugin(
@@ -154,7 +155,7 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> {
public:
ServerBuilderPluginTest() {}
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
port_ = grpc_pick_unused_port_or_die();
builder_.reset(new ServerBuilder());
}
@@ -191,7 +192,7 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> {
// we run some tests without a service, and for those we need to supply a
// frequently polled completion queue
cq_ = builder_->AddCompletionQueue();
- cq_thread_ = new grpc::thread(&ServerBuilderPluginTest::RunCQ, this);
+ cq_thread_ = new std::thread(&ServerBuilderPluginTest::RunCQ, this);
server_ = builder_->BuildAndStart();
EXPECT_TRUE(CheckPresent());
}
@@ -202,7 +203,7 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> {
stub_ = grpc::testing::EchoTestService::NewStub(channel_);
}
- void TearDown() GRPC_OVERRIDE {
+ void TearDown() override {
auto plugin = CheckPresent();
EXPECT_TRUE(plugin);
EXPECT_TRUE(plugin->init_server_is_called());
@@ -225,7 +226,7 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> {
std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_;
std::unique_ptr<ServerCompletionQueue> cq_;
std::unique_ptr<Server> server_;
- grpc::thread* cq_thread_;
+ std::thread* cq_thread_;
TestServiceImpl service_;
int port_;
diff --git a/test/cpp/end2end/server_crash_test.cc b/test/cpp/end2end/server_crash_test.cc
index 16a5fa2322..b1f9216055 100644
--- a/test/cpp/end2end/server_crash_test.cc
+++ b/test/cpp/end2end/server_crash_test.cc
@@ -60,14 +60,13 @@ namespace testing {
namespace {
-class ServiceImpl GRPC_FINAL
- : public ::grpc::testing::EchoTestService::Service {
+class ServiceImpl final : public ::grpc::testing::EchoTestService::Service {
public:
ServiceImpl() : bidi_stream_count_(0), response_stream_count_(0) {}
- Status BidiStream(ServerContext* context,
- ServerReaderWriter<EchoResponse, EchoRequest>* stream)
- GRPC_OVERRIDE {
+ Status BidiStream(
+ ServerContext* context,
+ ServerReaderWriter<EchoResponse, EchoRequest>* stream) override {
bidi_stream_count_++;
EchoRequest request;
EchoResponse response;
@@ -82,7 +81,7 @@ class ServiceImpl GRPC_FINAL
}
Status ResponseStream(ServerContext* context, const EchoRequest* request,
- ServerWriter<EchoResponse>* writer) GRPC_OVERRIDE {
+ ServerWriter<EchoResponse>* writer) override {
EchoResponse response;
response_stream_count_++;
for (int i = 0;; i++) {
@@ -139,7 +138,7 @@ TEST_F(CrashTest, ResponseStream) {
auto server = CreateServerAndClient("response");
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
- gpr_time_from_seconds(5, GPR_TIMESPAN)));
+ gpr_time_from_seconds(60, GPR_TIMESPAN)));
KillClient();
server->Shutdown();
GPR_ASSERT(HadOneResponseStream());
@@ -149,7 +148,7 @@ TEST_F(CrashTest, BidiStream) {
auto server = CreateServerAndClient("bidi");
gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
- gpr_time_from_seconds(5, GPR_TIMESPAN)));
+ gpr_time_from_seconds(60, GPR_TIMESPAN)));
KillClient();
server->Shutdown();
GPR_ASSERT(HadOneBidiStream());
diff --git a/test/cpp/end2end/shutdown_test.cc b/test/cpp/end2end/shutdown_test.cc
index 4cba3b1c81..bd68e851d3 100644
--- a/test/cpp/end2end/shutdown_test.cc
+++ b/test/cpp/end2end/shutdown_test.cc
@@ -49,6 +49,7 @@
#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/test_credentials_provider.h"
using grpc::testing::EchoRequest;
using grpc::testing::EchoResponse;
@@ -61,7 +62,7 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
explicit TestServiceImpl(gpr_event* ev) : ev_(ev) {}
Status Echo(ServerContext* context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
gpr_event_set(ev_, (void*)1);
while (!context->IsCancelled()) {
}
@@ -72,11 +73,11 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
gpr_event* ev_;
};
-class ShutdownTest : public ::testing::Test {
+class ShutdownTest : public ::testing::TestWithParam<string> {
public:
ShutdownTest() : shutdown_(false), service_(&ev_) { gpr_event_init(&ev_); }
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
port_ = grpc_pick_unused_port_or_die();
server_ = SetUpServer(port_);
}
@@ -85,17 +86,22 @@ class ShutdownTest : public ::testing::Test {
grpc::string server_address = "localhost:" + to_string(port);
ServerBuilder builder;
- builder.AddListeningPort(server_address, InsecureServerCredentials());
+ auto server_creds =
+ GetCredentialsProvider()->GetServerCredentials(GetParam());
+ builder.AddListeningPort(server_address, server_creds);
builder.RegisterService(&service_);
std::unique_ptr<Server> server = builder.BuildAndStart();
return server;
}
- void TearDown() GRPC_OVERRIDE { GPR_ASSERT(shutdown_); }
+ void TearDown() override { GPR_ASSERT(shutdown_); }
void ResetStub() {
string target = "dns:localhost:" + to_string(port_);
- channel_ = CreateChannel(target, InsecureChannelCredentials());
+ ChannelArguments args;
+ auto channel_creds =
+ GetCredentialsProvider()->GetChannelCredentials(GetParam(), &args);
+ channel_ = CreateCustomChannel(target, channel_creds, args);
stub_ = grpc::testing::EchoTestService::NewStub(channel_);
}
@@ -125,8 +131,31 @@ class ShutdownTest : public ::testing::Test {
TestServiceImpl service_;
};
+std::vector<string> GetAllCredentialsTypeList() {
+ std::vector<grpc::string> credentials_types;
+ if (GetCredentialsProvider()->GetChannelCredentials(kInsecureCredentialsType,
+ nullptr) != nullptr) {
+ credentials_types.push_back(kInsecureCredentialsType);
+ }
+ auto sec_list = GetCredentialsProvider()->GetSecureCredentialsTypeList();
+ for (auto sec = sec_list.begin(); sec != sec_list.end(); sec++) {
+ credentials_types.push_back(*sec);
+ }
+ GPR_ASSERT(!credentials_types.empty());
+
+ std::string credentials_type_list("credentials types:");
+ for (const string& type : credentials_types) {
+ credentials_type_list.append(" " + type);
+ }
+ gpr_log(GPR_INFO, "%s", credentials_type_list.c_str());
+ return credentials_types;
+}
+
+INSTANTIATE_TEST_CASE_P(End2EndShutdown, ShutdownTest,
+ ::testing::ValuesIn(GetAllCredentialsTypeList()));
+
// TODO(ctiller): leaked objects in this test
-TEST_F(ShutdownTest, ShutdownTest) {
+TEST_P(ShutdownTest, ShutdownTest) {
ResetStub();
// send the request in a background thread
diff --git a/test/cpp/end2end/streaming_throughput_test.cc b/test/cpp/end2end/streaming_throughput_test.cc
index fbef761ca9..302583766b 100644
--- a/test/cpp/end2end/streaming_throughput_test.cc
+++ b/test/cpp/end2end/streaming_throughput_test.cc
@@ -121,9 +121,9 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
}
// Only implement the one method we will be calling for brevity.
- Status BidiStream(ServerContext* context,
- ServerReaderWriter<EchoResponse, EchoRequest>* stream)
- GRPC_OVERRIDE {
+ Status BidiStream(
+ ServerContext* context,
+ ServerReaderWriter<EchoResponse, EchoRequest>* stream) override {
EchoRequest request;
gpr_atm should_exit;
gpr_atm_rel_store(&should_exit, static_cast<gpr_atm>(0));
@@ -147,7 +147,7 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
class End2endTest : public ::testing::Test {
protected:
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
int port = grpc_pick_unused_port_or_die();
server_address_ << "localhost:" << port;
// Setup server
@@ -158,7 +158,7 @@ class End2endTest : public ::testing::Test {
server_ = builder.BuildAndStart();
}
- void TearDown() GRPC_OVERRIDE { server_->Shutdown(); }
+ void TearDown() override { server_->Shutdown(); }
void ResetStub() {
std::shared_ptr<Channel> channel =
diff --git a/test/cpp/end2end/test_service_impl.cc b/test/cpp/end2end/test_service_impl.cc
index 2de344efd5..59d36e9cb5 100644
--- a/test/cpp/end2end/test_service_impl.cc
+++ b/test/cpp/end2end/test_service_impl.cc
@@ -88,6 +88,10 @@ void CheckServerAuthContext(
Status TestServiceImpl::Echo(ServerContext* context, const EchoRequest* request,
EchoResponse* response) {
+ if (request->has_param() && request->param().server_die()) {
+ gpr_log(GPR_ERROR, "The request should not reach application handler.");
+ GPR_ASSERT(0);
+ }
int server_try_cancel = GetIntValueFromMetadata(
kServerTryCancelRequest, context->client_metadata(), DO_NOT_CANCEL);
if (server_try_cancel > DO_NOT_CANCEL) {
@@ -194,7 +198,7 @@ Status TestServiceImpl::RequestStream(ServerContext* context,
return Status::CANCELLED;
}
- std::thread* server_try_cancel_thd = NULL;
+ std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread(&TestServiceImpl::ServerTryCancel, this, context);
@@ -212,7 +216,7 @@ Status TestServiceImpl::RequestStream(ServerContext* context,
}
gpr_log(GPR_INFO, "Read: %d messages", num_msgs_read);
- if (server_try_cancel_thd != NULL) {
+ if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
@@ -248,7 +252,7 @@ Status TestServiceImpl::ResponseStream(ServerContext* context,
}
EchoResponse response;
- std::thread* server_try_cancel_thd = NULL;
+ std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread(&TestServiceImpl::ServerTryCancel, this, context);
@@ -259,7 +263,7 @@ Status TestServiceImpl::ResponseStream(ServerContext* context,
writer->Write(response);
}
- if (server_try_cancel_thd != NULL) {
+ if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
@@ -295,7 +299,7 @@ Status TestServiceImpl::BidiStream(
return Status::CANCELLED;
}
- std::thread* server_try_cancel_thd = NULL;
+ std::thread* server_try_cancel_thd = nullptr;
if (server_try_cancel == CANCEL_DURING_PROCESSING) {
server_try_cancel_thd =
new std::thread(&TestServiceImpl::ServerTryCancel, this, context);
@@ -307,7 +311,7 @@ Status TestServiceImpl::BidiStream(
stream->Write(response);
}
- if (server_try_cancel_thd != NULL) {
+ if (server_try_cancel_thd != nullptr) {
server_try_cancel_thd->join();
delete server_try_cancel_thd;
return Status::CANCELLED;
diff --git a/test/cpp/end2end/test_service_impl.h b/test/cpp/end2end/test_service_impl.h
index c89f88c900..88e0be7bca 100644
--- a/test/cpp/end2end/test_service_impl.h
+++ b/test/cpp/end2end/test_service_impl.h
@@ -63,20 +63,20 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
: signal_client_(false), host_(new grpc::string(host)) {}
Status Echo(ServerContext* context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE;
+ EchoResponse* response) override;
// Unimplemented is left unimplemented to test the returned error.
Status RequestStream(ServerContext* context,
ServerReader<EchoRequest>* reader,
- EchoResponse* response) GRPC_OVERRIDE;
+ EchoResponse* response) override;
Status ResponseStream(ServerContext* context, const EchoRequest* request,
- ServerWriter<EchoResponse>* writer) GRPC_OVERRIDE;
+ ServerWriter<EchoResponse>* writer) override;
- Status BidiStream(ServerContext* context,
- ServerReaderWriter<EchoResponse, EchoRequest>* stream)
- GRPC_OVERRIDE;
+ Status BidiStream(
+ ServerContext* context,
+ ServerReaderWriter<EchoResponse, EchoRequest>* stream) override;
bool signal_client() {
std::unique_lock<std::mutex> lock(mu_);
diff --git a/test/cpp/end2end/thread_stress_test.cc b/test/cpp/end2end/thread_stress_test.cc
index b021b34523..d353f9894b 100644
--- a/test/cpp/end2end/thread_stress_test.cc
+++ b/test/cpp/end2end/thread_stress_test.cc
@@ -86,12 +86,12 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
TestServiceImpl() : signal_client_(false) {}
Status Echo(ServerContext* context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
response->set_message(request->message());
MaybeEchoDeadline(context, request, response);
if (request->has_param() && request->param().client_cancel_after_us()) {
{
- unique_lock<mutex> lock(mu_);
+ std::unique_lock<std::mutex> lock(mu_);
signal_client_ = true;
}
while (!context->IsCancelled()) {
@@ -118,7 +118,7 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
Status RequestStream(ServerContext* context,
ServerReader<EchoRequest>* reader,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
EchoRequest request;
response->set_message("");
while (reader->Read(&request)) {
@@ -130,7 +130,7 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
// Return 3 messages.
// TODO(yangg) make it generic by adding a parameter into EchoRequest
Status ResponseStream(ServerContext* context, const EchoRequest* request,
- ServerWriter<EchoResponse>* writer) GRPC_OVERRIDE {
+ ServerWriter<EchoResponse>* writer) override {
EchoResponse response;
response.set_message(request->message() + "0");
writer->Write(response);
@@ -142,9 +142,9 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
return Status::OK;
}
- Status BidiStream(ServerContext* context,
- ServerReaderWriter<EchoResponse, EchoRequest>* stream)
- GRPC_OVERRIDE {
+ Status BidiStream(
+ ServerContext* context,
+ ServerReaderWriter<EchoResponse, EchoRequest>* stream) override {
EchoRequest request;
EchoResponse response;
while (stream->Read(&request)) {
@@ -156,20 +156,20 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
}
bool signal_client() {
- unique_lock<mutex> lock(mu_);
+ std::unique_lock<std::mutex> lock(mu_);
return signal_client_;
}
private:
bool signal_client_;
- mutex mu_;
+ std::mutex mu_;
};
class TestServiceImplDupPkg
: public ::grpc::testing::duplicate::EchoTestService::Service {
public:
Status Echo(ServerContext* context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
response->set_message("no package");
return Status::OK;
}
@@ -215,12 +215,12 @@ class CommonStressTest {
class CommonStressTestSyncServer : public CommonStressTest<TestServiceImpl> {
public:
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
ServerBuilder builder;
SetUpStart(&builder, &service_);
SetUpEnd(&builder);
}
- void TearDown() GRPC_OVERRIDE {
+ void TearDown() override {
TearDownStart();
TearDownEnd();
}
@@ -232,32 +232,31 @@ class CommonStressTestSyncServer : public CommonStressTest<TestServiceImpl> {
class CommonStressTestAsyncServer
: public CommonStressTest<grpc::testing::EchoTestService::AsyncService> {
public:
- void SetUp() GRPC_OVERRIDE {
+ CommonStressTestAsyncServer() : contexts_(kNumAsyncServerThreads * 100) {}
+ void SetUp() override {
shutting_down_ = false;
ServerBuilder builder;
SetUpStart(&builder, &service_);
cq_ = builder.AddCompletionQueue();
SetUpEnd(&builder);
- contexts_ = new Context[kNumAsyncServerThreads * 100];
for (int i = 0; i < kNumAsyncServerThreads * 100; i++) {
RefreshContext(i);
}
for (int i = 0; i < kNumAsyncServerThreads; i++) {
- server_threads_.push_back(
- new std::thread(&CommonStressTestAsyncServer::ProcessRpcs, this));
+ server_threads_.emplace_back(&CommonStressTestAsyncServer::ProcessRpcs,
+ this);
}
}
- void TearDown() GRPC_OVERRIDE {
+ void TearDown() override {
{
- unique_lock<mutex> l(mu_);
+ std::unique_lock<std::mutex> l(mu_);
TearDownStart();
shutting_down_ = true;
cq_->Shutdown();
}
for (int i = 0; i < kNumAsyncServerThreads; i++) {
- server_threads_[i]->join();
- delete server_threads_[i];
+ server_threads_[i].join();
}
void* ignored_tag;
@@ -265,7 +264,6 @@ class CommonStressTestAsyncServer
while (cq_->Next(&ignored_tag, &ignored_ok))
;
TearDownEnd();
- delete[] contexts_;
}
private:
@@ -292,7 +290,7 @@ class CommonStressTestAsyncServer
}
}
void RefreshContext(int i) {
- unique_lock<mutex> l(mu_);
+ std::unique_lock<std::mutex> l(mu_);
if (!shutting_down_) {
contexts_[i].state = Context::READY;
contexts_[i].srv_ctx.reset(new ServerContext);
@@ -311,20 +309,21 @@ class CommonStressTestAsyncServer
response_writer;
EchoRequest recv_request;
enum { READY, DONE } state;
- } * contexts_;
+ };
+ std::vector<Context> contexts_;
::grpc::testing::EchoTestService::AsyncService service_;
std::unique_ptr<ServerCompletionQueue> cq_;
bool shutting_down_;
- mutex mu_;
- std::vector<std::thread*> server_threads_;
+ std::mutex mu_;
+ std::vector<std::thread> server_threads_;
};
template <class Common>
class End2endTest : public ::testing::Test {
protected:
End2endTest() {}
- void SetUp() GRPC_OVERRIDE { common_.SetUp(); }
- void TearDown() GRPC_OVERRIDE { common_.TearDown(); }
+ void SetUp() override { common_.SetUp(); }
+ void TearDown() override { common_.TearDown(); }
void ResetStub() { common_.ResetStub(); }
Common common_;
@@ -339,7 +338,11 @@ static void SendRpc(grpc::testing::EchoTestService::Stub* stub, int num_rpcs) {
ClientContext context;
Status s = stub->Echo(&context, request, &response);
EXPECT_EQ(response.message(), request.message());
- EXPECT_TRUE(s.ok());
+ if (!s.ok()) {
+ gpr_log(GPR_ERROR, "RPC error: %d: %s", s.error_code(),
+ s.error_message().c_str());
+ }
+ ASSERT_TRUE(s.ok());
}
}
@@ -349,14 +352,12 @@ typedef ::testing::Types<CommonStressTestSyncServer,
TYPED_TEST_CASE(End2endTest, CommonTypes);
TYPED_TEST(End2endTest, ThreadStress) {
this->common_.ResetStub();
- std::vector<std::thread*> threads;
+ std::vector<std::thread> threads;
for (int i = 0; i < kNumThreads; ++i) {
- threads.push_back(
- new std::thread(SendRpc, this->common_.GetStub(), kNumRpcs));
+ threads.emplace_back(SendRpc, this->common_.GetStub(), kNumRpcs);
}
for (int i = 0; i < kNumThreads; ++i) {
- threads[i]->join();
- delete threads[i];
+ threads[i].join();
}
}
@@ -365,8 +366,8 @@ class AsyncClientEnd2endTest : public ::testing::Test {
protected:
AsyncClientEnd2endTest() : rpcs_outstanding_(0) {}
- void SetUp() GRPC_OVERRIDE { common_.SetUp(); }
- void TearDown() GRPC_OVERRIDE {
+ void SetUp() override { common_.SetUp(); }
+ void TearDown() override {
void* ignored_tag;
bool ignored_ok;
while (cq_.Next(&ignored_tag, &ignored_ok))
@@ -375,7 +376,7 @@ class AsyncClientEnd2endTest : public ::testing::Test {
}
void Wait() {
- unique_lock<mutex> l(mu_);
+ std::unique_lock<std::mutex> l(mu_);
while (rpcs_outstanding_ != 0) {
cv_.wait(l);
}
@@ -400,7 +401,7 @@ class AsyncClientEnd2endTest : public ::testing::Test {
call->response_reader->Finish(&call->response, &call->status,
(void*)call);
- unique_lock<mutex> l(mu_);
+ std::unique_lock<std::mutex> l(mu_);
rpcs_outstanding_++;
}
}
@@ -418,7 +419,7 @@ class AsyncClientEnd2endTest : public ::testing::Test {
bool notify;
{
- unique_lock<mutex> l(mu_);
+ std::unique_lock<std::mutex> l(mu_);
rpcs_outstanding_--;
notify = (rpcs_outstanding_ == 0);
}
@@ -430,34 +431,32 @@ class AsyncClientEnd2endTest : public ::testing::Test {
Common common_;
CompletionQueue cq_;
- mutex mu_;
- condition_variable cv_;
+ std::mutex mu_;
+ std::condition_variable cv_;
int rpcs_outstanding_;
};
TYPED_TEST_CASE(AsyncClientEnd2endTest, CommonTypes);
TYPED_TEST(AsyncClientEnd2endTest, ThreadStress) {
this->common_.ResetStub();
- std::vector<std::thread *> send_threads, completion_threads;
+ std::vector<std::thread> send_threads, completion_threads;
for (int i = 0; i < kNumAsyncReceiveThreads; ++i) {
- completion_threads.push_back(new std::thread(
+ completion_threads.emplace_back(
&AsyncClientEnd2endTest_ThreadStress_Test<TypeParam>::AsyncCompleteRpc,
- this));
+ this);
}
for (int i = 0; i < kNumAsyncSendThreads; ++i) {
- send_threads.push_back(new std::thread(
+ send_threads.emplace_back(
&AsyncClientEnd2endTest_ThreadStress_Test<TypeParam>::AsyncSendRpc,
- this, kNumRpcs));
+ this, kNumRpcs);
}
for (int i = 0; i < kNumAsyncSendThreads; ++i) {
- send_threads[i]->join();
- delete send_threads[i];
+ send_threads[i].join();
}
this->Wait();
for (int i = 0; i < kNumAsyncReceiveThreads; ++i) {
- completion_threads[i]->join();
- delete completion_threads[i];
+ completion_threads[i].join();
}
}
diff --git a/test/cpp/grpclb/grpclb_api_test.cc b/test/cpp/grpclb/grpclb_api_test.cc
index e67189c69e..82ccf436f8 100644
--- a/test/cpp/grpclb/grpclb_api_test.cc
+++ b/test/cpp/grpclb/grpclb_api_test.cc
@@ -63,7 +63,7 @@ grpc::string PackedStringToIp(const grpc_grpclb_ip_address& pb_ip) {
} else {
abort();
}
- GPR_ASSERT(inet_ntop(af, pb_ip.bytes, ip_str, 46) != NULL);
+ GPR_ASSERT(inet_ntop(af, (void*)pb_ip.bytes, ip_str, 46) != NULL);
return ip_str;
}
@@ -71,12 +71,12 @@ TEST_F(GrpclbTest, CreateRequest) {
const grpc::string service_name = "AServiceName";
LoadBalanceRequest request;
grpc_grpclb_request* c_req = grpc_grpclb_request_create(service_name.c_str());
- gpr_slice slice = grpc_grpclb_request_encode(c_req);
- const int num_bytes_written = GPR_SLICE_LENGTH(slice);
+ grpc_slice slice = grpc_grpclb_request_encode(c_req);
+ const int num_bytes_written = GRPC_SLICE_LENGTH(slice);
EXPECT_GT(num_bytes_written, 0);
- request.ParseFromArray(GPR_SLICE_START_PTR(slice), num_bytes_written);
+ request.ParseFromArray(GRPC_SLICE_START_PTR(slice), num_bytes_written);
EXPECT_EQ(request.initial_request().name(), service_name);
- gpr_slice_unref(slice);
+ grpc_slice_unref(slice);
grpc_grpclb_request_destroy(c_req);
}
@@ -88,15 +88,15 @@ TEST_F(GrpclbTest, ParseInitialResponse) {
client_stats_report_interval->set_seconds(123);
client_stats_report_interval->set_nanos(456);
const grpc::string encoded_response = response.SerializeAsString();
- gpr_slice encoded_slice =
- gpr_slice_from_copied_string(encoded_response.c_str());
+ grpc_slice encoded_slice =
+ grpc_slice_from_copied_string(encoded_response.c_str());
grpc_grpclb_initial_response* c_initial_response =
grpc_grpclb_initial_response_parse(encoded_slice);
EXPECT_FALSE(c_initial_response->has_load_balancer_delegate);
EXPECT_EQ(c_initial_response->client_stats_report_interval.seconds, 123);
EXPECT_EQ(c_initial_response->client_stats_report_interval.nanos, 456);
- gpr_slice_unref(encoded_slice);
+ grpc_slice_unref(encoded_slice);
grpc_grpclb_initial_response_destroy(c_initial_response);
}
@@ -116,7 +116,7 @@ TEST_F(GrpclbTest, ParseResponseServerList) {
expiration_interval->set_nanos(999);
const grpc::string encoded_response = response.SerializeAsString();
- const gpr_slice encoded_slice = gpr_slice_from_copied_buffer(
+ const grpc_slice encoded_slice = grpc_slice_from_copied_buffer(
encoded_response.data(), encoded_response.size());
grpc_grpclb_serverlist* c_serverlist =
grpc_grpclb_response_parse_serverlist(encoded_slice);
@@ -137,7 +137,7 @@ TEST_F(GrpclbTest, ParseResponseServerList) {
EXPECT_TRUE(c_serverlist->expiration_interval.has_nanos);
EXPECT_EQ(c_serverlist->expiration_interval.nanos, 999);
- gpr_slice_unref(encoded_slice);
+ grpc_slice_unref(encoded_slice);
grpc_grpclb_destroy_serverlist(c_serverlist);
}
diff --git a/test/cpp/grpclb/grpclb_test.cc b/test/cpp/grpclb/grpclb_test.cc
index 80f2fa4f4d..89ed9249ad 100644
--- a/test/cpp/grpclb/grpclb_test.cc
+++ b/test/cpp/grpclb/grpclb_test.cc
@@ -51,9 +51,11 @@
#include <grpc++/impl/codegen/config.h>
extern "C" {
-#include "src/core/ext/client_config/client_channel.h"
+#include "src/core/ext/client_channel/client_channel.h"
+#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/iomgr/sockaddr.h"
+#include "src/core/lib/security/credentials/fake/fake_credentials.h"
#include "src/core/lib/support/string.h"
#include "src/core/lib/support/tmpfile.h"
#include "src/core/lib/surface/channel.h"
@@ -76,10 +78,25 @@ extern "C" {
// - Send a serverlist with faulty ip:port addresses (port > 2^16, etc).
// - Test reception of invalid serverlist
// - Test pinging
-// - Test against a non-LB server. That server should return UNIMPLEMENTED and
-// the call should fail.
+// - Test against a non-LB server.
// - Random LB server closing the stream unexpectedly.
// - Test using DNS-resolvable names (localhost?)
+// - Test handling of creation of faulty RR instance by having the LB return a
+// serverlist with non-existent backends after having initially returned a
+// valid one.
+//
+// Findings from end to end testing to be covered here:
+// - Handling of LB servers restart, including reconnection after backing-off
+// retries.
+// - Destruction of load balanced channel (and therefore of grpclb instance)
+// while:
+// 1) the internal LB call is still active. This should work by virtue
+// of the weak reference the LB call holds. The call should be terminated as
+// part of the grpclb shutdown process.
+// 2) the retry timer is active. Again, the weak reference it holds should
+// prevent a premature call to \a glb_destroy.
+// - Restart of backend servers with no changes to serverlist. This exercises
+// the RR handover mechanism.
namespace grpc {
namespace {
@@ -95,7 +112,9 @@ typedef struct server_fixture {
grpc_call *server_call;
grpc_completion_queue *cq;
char *servers_hostport;
+ const char *balancer_name;
int port;
+ const char *lb_token_prefix;
gpr_thd_id tid;
int num_calls_serviced;
} server_fixture;
@@ -109,9 +128,10 @@ typedef struct test_fixture {
static void *tag(intptr_t t) { return (void *)t; }
-static gpr_slice build_response_payload_slice(
+static grpc_slice build_response_payload_slice(
const char *host, int *ports, size_t nports,
- int64_t expiration_interval_secs, int32_t expiration_interval_nanos) {
+ int64_t expiration_interval_secs, int32_t expiration_interval_nanos,
+ const char *token_prefix) {
// server_list {
// servers {
// ip_address: <in_addr/6 bytes of an IP>
@@ -138,23 +158,22 @@ static gpr_slice build_response_payload_slice(
struct in_addr ip4;
GPR_ASSERT(inet_pton(AF_INET, host, &ip4) == 1);
server->set_ip_address(
- grpc::string(reinterpret_cast<const char *>(&ip4), sizeof(ip4)));
+ string(reinterpret_cast<const char *>(&ip4), sizeof(ip4)));
server->set_port(ports[i]);
- // The following long long int cast is meant to work around the
- // disfunctional implementation of std::to_string in gcc 4.4, which doesn't
- // have a version for int but does have one for long long int.
- string token_data = "token" + std::to_string((long long int)ports[i]);
- token_data.resize(64, '-');
- server->set_load_balance_token(token_data);
+ // Missing tokens are acceptable. Test that path.
+ if (strlen(token_prefix) > 0) {
+ string token_data = token_prefix + std::to_string(ports[i]);
+ server->set_load_balance_token(token_data);
+ }
}
- const grpc::string &enc_resp = response.SerializeAsString();
- return gpr_slice_from_copied_buffer(enc_resp.data(), enc_resp.size());
+ const string &enc_resp = response.SerializeAsString();
+ return grpc_slice_from_copied_buffer(enc_resp.data(), enc_resp.size());
}
static void drain_cq(grpc_completion_queue *cq) {
grpc_event ev;
do {
- ev = grpc_completion_queue_next(cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5),
+ ev = grpc_completion_queue_next(cq, grpc_timeout_seconds_to_deadline(5),
NULL);
} while (ev.type != GRPC_QUEUE_SHUTDOWN);
}
@@ -185,10 +204,12 @@ static void start_lb_server(server_fixture *sf, int *ports, size_t nports,
&request_metadata_recv, sf->cq, sf->cq,
tag(200));
GPR_ASSERT(GRPC_CALL_OK == error);
- gpr_log(GPR_INFO, "LB Server[%s] up", sf->servers_hostport);
+ gpr_log(GPR_INFO, "LB Server[%s](%s) up", sf->servers_hostport,
+ sf->balancer_name);
CQ_EXPECT_COMPLETION(cqv, tag(200), 1);
cq_verify(cqv);
- gpr_log(GPR_INFO, "LB Server[%s] after tag 200", sf->servers_hostport);
+ gpr_log(GPR_INFO, "LB Server[%s](%s) after tag 200", sf->servers_hostport,
+ sf->balancer_name);
// make sure we've received the initial metadata from the grpclb request.
GPR_ASSERT(request_metadata_recv.count > 0);
@@ -197,7 +218,7 @@ static void start_lb_server(server_fixture *sf, int *ports, size_t nports,
// receive request for backends
op = ops;
op->op = GRPC_OP_RECV_MESSAGE;
- op->data.recv_message = &request_payload_recv;
+ op->data.recv_message.recv_message = &request_payload_recv;
op->flags = 0;
op->reserved = NULL;
op++;
@@ -205,22 +226,23 @@ static void start_lb_server(server_fixture *sf, int *ports, size_t nports,
GPR_ASSERT(GRPC_CALL_OK == error);
CQ_EXPECT_COMPLETION(cqv, tag(202), 1);
cq_verify(cqv);
- gpr_log(GPR_INFO, "LB Server[%s] after RECV_MSG", sf->servers_hostport);
+ gpr_log(GPR_INFO, "LB Server[%s](%s) after RECV_MSG", sf->servers_hostport,
+ sf->balancer_name);
// validate initial request.
grpc_byte_buffer_reader bbr;
grpc_byte_buffer_reader_init(&bbr, request_payload_recv);
- gpr_slice request_payload_slice = grpc_byte_buffer_reader_readall(&bbr);
+ grpc_slice request_payload_slice = grpc_byte_buffer_reader_readall(&bbr);
grpc::lb::v1::LoadBalanceRequest request;
- request.ParseFromArray(GPR_SLICE_START_PTR(request_payload_slice),
- GPR_SLICE_LENGTH(request_payload_slice));
+ request.ParseFromArray(GRPC_SLICE_START_PTR(request_payload_slice),
+ GRPC_SLICE_LENGTH(request_payload_slice));
GPR_ASSERT(request.has_initial_request());
GPR_ASSERT(request.initial_request().name() == sf->servers_hostport);
- gpr_slice_unref(request_payload_slice);
+ grpc_slice_unref(request_payload_slice);
grpc_byte_buffer_reader_destroy(&bbr);
grpc_byte_buffer_destroy(request_payload_recv);
- gpr_slice response_payload_slice;
+ grpc_slice response_payload_slice;
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
@@ -234,25 +256,26 @@ static void start_lb_server(server_fixture *sf, int *ports, size_t nports,
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(201), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
- gpr_log(GPR_INFO, "LB Server[%s] after tag 201", sf->servers_hostport);
+ gpr_log(GPR_INFO, "LB Server[%s](%s) after tag 201", sf->servers_hostport,
+ sf->balancer_name);
for (int i = 0; i < 2; i++) {
if (i == 0) {
// First half of the ports.
- response_payload_slice =
- build_response_payload_slice("127.0.0.1", ports, nports / 2, -1, -1);
+ response_payload_slice = build_response_payload_slice(
+ "127.0.0.1", ports, nports / 2, -1, -1, sf->lb_token_prefix);
} else {
// Second half of the ports.
sleep_ms(update_delay_ms);
- response_payload_slice =
- build_response_payload_slice("127.0.0.1", ports + (nports / 2),
- (nports + 1) / 2 /* ceil */, -1, -1);
+ response_payload_slice = build_response_payload_slice(
+ "127.0.0.1", ports + (nports / 2), (nports + 1) / 2 /* ceil */, -1,
+ -1, "" /* this half doesn't get to receive an LB token */);
}
response_payload = grpc_raw_byte_buffer_create(&response_payload_slice, 1);
op = ops;
op->op = GRPC_OP_SEND_MESSAGE;
- op->data.send_message = response_payload;
+ op->data.send_message.send_message = response_payload;
op->flags = 0;
op->reserved = NULL;
op++;
@@ -260,19 +283,21 @@ static void start_lb_server(server_fixture *sf, int *ports, size_t nports,
GPR_ASSERT(GRPC_CALL_OK == error);
CQ_EXPECT_COMPLETION(cqv, tag(203), 1);
cq_verify(cqv);
- gpr_log(GPR_INFO, "LB Server[%s] after SEND_MESSAGE, iter %d",
- sf->servers_hostport, i);
+ gpr_log(GPR_INFO, "LB Server[%s](%s) after SEND_MESSAGE, iter %d",
+ sf->servers_hostport, sf->balancer_name, i);
grpc_byte_buffer_destroy(response_payload);
- gpr_slice_unref(response_payload_slice);
+ grpc_slice_unref(response_payload_slice);
}
- gpr_log(GPR_INFO, "LB Server[%s] shutting down", sf->servers_hostport);
+ gpr_log(GPR_INFO, "LB Server[%s](%s) shutting down", sf->servers_hostport,
+ sf->balancer_name);
op = ops;
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
op->data.send_status_from_server.trailing_metadata_count = 0;
op->data.send_status_from_server.status = GRPC_STATUS_OK;
- op->data.send_status_from_server.status_details = "xyz";
+ grpc_slice status_details = grpc_slice_from_static_string("xyz");
+ op->data.send_status_from_server.status_details = &status_details;
op->flags = 0;
op->reserved = NULL;
op++;
@@ -282,8 +307,8 @@ static void start_lb_server(server_fixture *sf, int *ports, size_t nports,
CQ_EXPECT_COMPLETION(cqv, tag(201), 1);
CQ_EXPECT_COMPLETION(cqv, tag(204), 1);
cq_verify(cqv);
- gpr_log(GPR_INFO, "LB Server[%s] after tag 204. All done. LB server out",
- sf->servers_hostport);
+ gpr_log(GPR_INFO, "LB Server[%s](%s) after tag 204. All done. LB server out",
+ sf->servers_hostport, sf->balancer_name);
grpc_call_destroy(s);
@@ -319,7 +344,7 @@ static void start_backend_server(server_fixture *sf) {
GPR_ASSERT(GRPC_CALL_OK == error);
gpr_log(GPR_INFO, "Server[%s] up", sf->servers_hostport);
ev = grpc_completion_queue_next(sf->cq,
- GRPC_TIMEOUT_SECONDS_TO_DEADLINE(60), NULL);
+ grpc_timeout_seconds_to_deadline(60), NULL);
if (!ev.success) {
gpr_log(GPR_INFO, "Server[%s] being torn down", sf->servers_hostport);
cq_verifier_destroy(cqv);
@@ -328,12 +353,9 @@ static void start_backend_server(server_fixture *sf) {
return;
}
GPR_ASSERT(ev.type == GRPC_OP_COMPLETE);
-
- // The following long long int cast is meant to work around the
- // disfunctional implementation of std::to_string in gcc 4.4, which doesn't
- // have a version for int but does have one for long long int.
- string expected_token = "token" + std::to_string((long long int)sf->port);
- expected_token.resize(64, '-');
+ const string expected_token =
+ strlen(sf->lb_token_prefix) == 0 ? "" : sf->lb_token_prefix +
+ std::to_string(sf->port);
GPR_ASSERT(contains_metadata(&request_metadata_recv, "lb-token",
expected_token.c_str()));
@@ -355,18 +377,18 @@ static void start_backend_server(server_fixture *sf) {
gpr_log(GPR_INFO, "Server[%s] after tag 101", sf->servers_hostport);
bool exit = false;
- gpr_slice response_payload_slice = gpr_slice_from_copied_string(PAYLOAD);
+ grpc_slice response_payload_slice = grpc_slice_from_copied_string(PAYLOAD);
while (!exit) {
op = ops;
op->op = GRPC_OP_RECV_MESSAGE;
- op->data.recv_message = &request_payload_recv;
+ op->data.recv_message.recv_message = &request_payload_recv;
op->flags = 0;
op->reserved = NULL;
op++;
error = grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(102), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
ev = grpc_completion_queue_next(
- sf->cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3), NULL);
+ sf->cq, grpc_timeout_seconds_to_deadline(3), NULL);
if (ev.type == GRPC_OP_COMPLETE && ev.success) {
GPR_ASSERT(ev.tag = tag(102));
if (request_payload_recv == NULL) {
@@ -388,7 +410,7 @@ static void start_backend_server(server_fixture *sf) {
grpc_raw_byte_buffer_create(&response_payload_slice, 1);
op = ops;
op->op = GRPC_OP_SEND_MESSAGE;
- op->data.send_message = response_payload;
+ op->data.send_message.send_message = response_payload;
op->flags = 0;
op->reserved = NULL;
op++;
@@ -396,7 +418,7 @@ static void start_backend_server(server_fixture *sf) {
grpc_call_start_batch(s, ops, (size_t)(op - ops), tag(103), NULL);
GPR_ASSERT(GRPC_CALL_OK == error);
ev = grpc_completion_queue_next(
- sf->cq, GRPC_TIMEOUT_SECONDS_TO_DEADLINE(3), NULL);
+ sf->cq, grpc_timeout_seconds_to_deadline(3), NULL);
if (ev.type == GRPC_OP_COMPLETE && ev.success) {
GPR_ASSERT(ev.tag = tag(103));
} else {
@@ -414,13 +436,15 @@ static void start_backend_server(server_fixture *sf) {
++sf->num_calls_serviced;
gpr_log(GPR_INFO, "Server[%s] OUT OF THE LOOP", sf->servers_hostport);
- gpr_slice_unref(response_payload_slice);
+ grpc_slice_unref(response_payload_slice);
op = ops;
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
op->data.send_status_from_server.trailing_metadata_count = 0;
op->data.send_status_from_server.status = GRPC_STATUS_OK;
- op->data.send_status_from_server.status_details = "Backend server out a-ok";
+ grpc_slice status_details =
+ grpc_slice_from_static_string("Backend server out a-ok");
+ op->data.send_status_from_server.status_details = &status_details;
op->flags = 0;
op->reserved = NULL;
op++;
@@ -449,18 +473,20 @@ static void perform_request(client_fixture *cf) {
grpc_metadata_array trailing_metadata_recv;
grpc_status_code status;
grpc_call_error error;
- char *details = NULL;
- size_t details_capacity = 0;
+ grpc_slice details;
grpc_byte_buffer *request_payload;
grpc_byte_buffer *response_payload_recv;
int i;
memset(ops, 0, sizeof(ops));
- gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
+ grpc_slice request_payload_slice =
+ grpc_slice_from_copied_string("hello world");
+ grpc_slice host = grpc_slice_from_static_string("foo.test.google.fr:1234");
c = grpc_channel_create_call(cf->client, NULL, GRPC_PROPAGATE_DEFAULTS,
- cf->cq, "/foo", "foo.test.google.fr:1234",
- GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL);
+ cf->cq, grpc_slice_from_static_string("/foo"),
+ &host, grpc_timeout_seconds_to_deadline(5),
+ NULL);
gpr_log(GPR_INFO, "Call 0x%" PRIxPTR " created", (intptr_t)c);
GPR_ASSERT(c);
char *peer;
@@ -475,7 +501,7 @@ static void perform_request(client_fixture *cf) {
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_INITIAL_METADATA;
- op->data.recv_initial_metadata = &initial_metadata_recv;
+ op->data.recv_initial_metadata.recv_initial_metadata = &initial_metadata_recv;
op->flags = 0;
op->reserved = NULL;
op++;
@@ -483,7 +509,6 @@ static void perform_request(client_fixture *cf) {
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
op->data.recv_status_on_client.status = &status;
op->data.recv_status_on_client.status_details = &details;
- op->data.recv_status_on_client.status_details_capacity = &details_capacity;
op->flags = 0;
op->reserved = NULL;
op++;
@@ -495,12 +520,12 @@ static void perform_request(client_fixture *cf) {
op = ops;
op->op = GRPC_OP_SEND_MESSAGE;
- op->data.send_message = request_payload;
+ op->data.send_message.send_message = request_payload;
op->flags = 0;
op->reserved = NULL;
op++;
op->op = GRPC_OP_RECV_MESSAGE;
- op->data.recv_message = &response_payload_recv;
+ op->data.recv_message.recv_message = &response_payload_recv;
op->flags = 0;
op->reserved = NULL;
op++;
@@ -509,13 +534,14 @@ static void perform_request(client_fixture *cf) {
CQ_EXPECT_COMPLETION(cqv, tag(2), 1);
cq_verify(cqv);
+ gpr_log(GPR_INFO, "Client after sending msg %d / 4", i + 1);
GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, PAYLOAD));
grpc_byte_buffer_destroy(request_payload);
grpc_byte_buffer_destroy(response_payload_recv);
}
- gpr_slice_unref(request_payload_slice);
+ grpc_slice_unref(request_payload_slice);
op = ops;
op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
@@ -530,22 +556,51 @@ static void perform_request(client_fixture *cf) {
cq_verify(cqv);
peer = grpc_call_get_peer(c);
gpr_log(GPR_INFO, "Client DONE WITH SERVER %s ", peer);
- gpr_free(peer);
grpc_call_destroy(c);
- cq_verify_empty_timeout(cqv, 1);
+ cq_verify_empty_timeout(cqv, 1 /* seconds */);
cq_verifier_destroy(cqv);
grpc_metadata_array_destroy(&initial_metadata_recv);
grpc_metadata_array_destroy(&trailing_metadata_recv);
- gpr_free(details);
+ grpc_slice_unref(details);
+ gpr_log(GPR_INFO, "Client call (peer %s) DESTROYED.", peer);
+ gpr_free(peer);
}
-static void setup_client(const char *server_hostport, client_fixture *cf) {
+#define BALANCERS_NAME "lb.name"
+static void setup_client(const server_fixture *lb_server,
+ const server_fixture *backends, client_fixture *cf) {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ char *lb_uri;
+ // The grpclb LB policy will be automatically selected by virtue of
+ // the fact that the returned addresses are balancer addresses.
+ gpr_asprintf(&lb_uri, "test:///%s?lb_enabled=1&balancer_names=%s",
+ lb_server->servers_hostport, lb_server->balancer_name);
+
+ grpc_arg expected_target_arg;
+ expected_target_arg.type = GRPC_ARG_STRING;
+ expected_target_arg.key =
+ const_cast<char *>(GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS);
+
+ char *expected_target_names = NULL;
+ const char *backends_name = lb_server->servers_hostport;
+ gpr_asprintf(&expected_target_names, "%s;%s", backends_name, BALANCERS_NAME);
+
+ expected_target_arg.value.string = const_cast<char *>(expected_target_names);
+ grpc_channel_args *args =
+ grpc_channel_args_copy_and_add(NULL, &expected_target_arg, 1);
+ gpr_free(expected_target_names);
+
cf->cq = grpc_completion_queue_create(NULL);
- cf->server_uri = gpr_strdup(server_hostport);
- cf->client = grpc_insecure_channel_create(cf->server_uri, NULL, NULL);
+ cf->server_uri = lb_uri;
+ grpc_channel_credentials *fake_creds =
+ grpc_fake_transport_security_credentials_create();
+ cf->client =
+ grpc_secure_channel_create(fake_creds, cf->server_uri, args, NULL);
+ grpc_channel_credentials_unref(&exec_ctx, fake_creds);
+ grpc_channel_args_destroy(&exec_ctx, args);
}
static void teardown_client(client_fixture *cf) {
@@ -572,10 +627,14 @@ static void setup_server(const char *host, server_fixture *sf) {
gpr_join_host_port(&sf->servers_hostport, host, sf->port);
}
+ grpc_server_credentials *server_creds =
+ grpc_fake_transport_security_server_credentials_create();
+
sf->server = grpc_server_create(NULL, NULL);
grpc_server_register_completion_queue(sf->server, sf->cq, NULL);
- GPR_ASSERT((assigned_port = grpc_server_add_insecure_http2_port(
- sf->server, sf->servers_hostport)) > 0);
+ GPR_ASSERT((assigned_port = grpc_server_add_secure_http2_port(
+ sf->server, sf->servers_hostport, server_creds)) > 0);
+ grpc_server_credentials_release(server_creds);
GPR_ASSERT(sf->port == assigned_port);
grpc_server_start(sf->server);
}
@@ -586,7 +645,7 @@ static void teardown_server(server_fixture *sf) {
gpr_log(GPR_INFO, "Server[%s] shutting down", sf->servers_hostport);
grpc_server_shutdown_and_notify(sf->server, sf->cq, tag(1000));
GPR_ASSERT(grpc_completion_queue_pluck(
- sf->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL)
+ sf->cq, tag(1000), grpc_timeout_seconds_to_deadline(5), NULL)
.type == GRPC_OP_COMPLETE);
grpc_server_destroy(sf->server);
gpr_thd_join(sf->tid);
@@ -615,6 +674,7 @@ static void fork_lb_server(void *arg) {
tf->lb_server_update_delay_ms);
}
+#define LB_TOKEN_PREFIX "token"
static test_fixture setup_test_fixture(int lb_server_update_delay_ms) {
test_fixture tf;
memset(&tf, 0, sizeof(tf));
@@ -624,20 +684,22 @@ static test_fixture setup_test_fixture(int lb_server_update_delay_ms) {
gpr_thd_options_set_joinable(&options);
for (int i = 0; i < NUM_BACKENDS; ++i) {
+ // Only the first half of the servers expect an LB token.
+ if (i < NUM_BACKENDS / 2) {
+ tf.lb_backends[i].lb_token_prefix = LB_TOKEN_PREFIX;
+ } else {
+ tf.lb_backends[i].lb_token_prefix = "";
+ }
setup_server("127.0.0.1", &tf.lb_backends[i]);
gpr_thd_new(&tf.lb_backends[i].tid, fork_backend_server, &tf.lb_backends[i],
&options);
}
+ tf.lb_server.lb_token_prefix = LB_TOKEN_PREFIX;
+ tf.lb_server.balancer_name = BALANCERS_NAME;
setup_server("127.0.0.1", &tf.lb_server);
gpr_thd_new(&tf.lb_server.tid, fork_lb_server, &tf.lb_server, &options);
-
- char *server_uri;
- gpr_asprintf(&server_uri, "test:%s?lb_policy=grpclb&lb_enabled=1",
- tf.lb_server.servers_hostport);
- setup_client(server_uri, &tf.client);
- gpr_free(server_uri);
-
+ setup_client(&tf.lb_server, tf.lb_backends, &tf.client);
return tf;
}
@@ -673,39 +735,44 @@ static test_fixture test_update(int lb_server_update_delay_ms) {
TEST(GrpclbTest, Updates) {
grpc::test_fixture tf_result;
- // Clients take a bit over one second to complete a call (the last part of the
+ // Clients take at least one second to complete a call (the last part of the
// call sleeps for 1 second while verifying the client's completion queue is
- // empty). Therefore:
+ // empty), more if the system is under load. Therefore:
//
// If the LB server waits 800ms before sending an update, it will arrive
- // before the first client request is done, skipping the second server from
- // batch 1 altogether: the 2nd client request will go to the 1st server of
- // batch 2 (ie, the third one out of the four total servers).
+ // before the first client request finishes, skipping the second server from
+ // batch 1. All subsequent picks will come from the second half of the
+ // backends, those coming in the LB update.
tf_result = grpc::test_update(800);
- GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced == 1);
- GPR_ASSERT(tf_result.lb_backends[1].num_calls_serviced == 0);
- GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced == 2);
- GPR_ASSERT(tf_result.lb_backends[3].num_calls_serviced == 1);
-
- // If the LB server waits 1500ms, the update arrives after having picked the
- // 2nd server from batch 1 but before the next pick for the first server of
- // batch 2. All server are used.
- tf_result = grpc::test_update(1500);
- GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced == 1);
- GPR_ASSERT(tf_result.lb_backends[1].num_calls_serviced == 1);
- GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced == 1);
- GPR_ASSERT(tf_result.lb_backends[3].num_calls_serviced == 1);
-
- // If the LB server waits > 2000ms, the update arrives after the first two
- // request are done and the third pick is performed, which returns, in RR
- // fashion, the 1st server of the 1st update. Therefore, the second server of
- // batch 1 is hit at least one, whereas the first server of batch 2 is never
- // hit.
+ GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced +
+ tf_result.lb_backends[1].num_calls_serviced ==
+ 1);
+ GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced +
+ tf_result.lb_backends[3].num_calls_serviced >
+ 0);
+ int num_serviced_calls = 0;
+ for (int i = 0; i < 4; i++) {
+ num_serviced_calls += tf_result.lb_backends[i].num_calls_serviced;
+ }
+ GPR_ASSERT(num_serviced_calls == 4);
+
+ // If the LB server waits 2500ms, the update arrives after two calls and three
+ // picks. The third pick will be the 1st server of the 1st update (RR policy
+ // going around). The fourth and final pick will come from the second LB
+ // update. In any case, the total number of serviced calls must again be equal
+ // to four across all the backends.
tf_result = grpc::test_update(2500);
- GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced >= 1);
- GPR_ASSERT(tf_result.lb_backends[1].num_calls_serviced > 0);
- GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced > 0);
- GPR_ASSERT(tf_result.lb_backends[3].num_calls_serviced == 0);
+ GPR_ASSERT(tf_result.lb_backends[0].num_calls_serviced +
+ tf_result.lb_backends[1].num_calls_serviced >=
+ 2);
+ GPR_ASSERT(tf_result.lb_backends[2].num_calls_serviced +
+ tf_result.lb_backends[3].num_calls_serviced >
+ 0);
+ num_serviced_calls = 0;
+ for (int i = 0; i < 4; i++) {
+ num_serviced_calls += tf_result.lb_backends[i].num_calls_serviced;
+ }
+ GPR_ASSERT(num_serviced_calls == 4);
}
TEST(GrpclbTest, InvalidAddressInServerlist) {}
diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc
index 245e27b2bb..5688ab7971 100644
--- a/test/cpp/interop/client.cc
+++ b/test/cpp/interop/client.cc
@@ -32,8 +32,7 @@
*/
#include <memory>
-
-#include <unistd.h>
+#include <unordered_map>
#include <gflags/gflags.h>
#include <grpc++/channel.h>
@@ -49,38 +48,41 @@
#include "test/cpp/util/test_config.h"
DEFINE_bool(use_tls, false, "Whether to use tls.");
+DEFINE_string(custom_credentials_type, "", "User provided credentials type.");
DEFINE_bool(use_test_ca, false, "False to use SSL roots for google");
DEFINE_int32(server_port, 0, "Server port.");
-DEFINE_string(server_host, "127.0.0.1", "Server host to connect to");
+DEFINE_string(server_host, "localhost", "Server host to connect to");
DEFINE_string(server_host_override, "foo.test.google.fr",
"Override the server host which is sent in HTTP header");
-DEFINE_string(test_case, "large_unary",
- "Configure different test cases. Valid options are:\n\n"
- "all : all test cases;\n"
- "cancel_after_begin : cancel stream after starting it;\n"
- "cancel_after_first_response: cancel on first response;\n"
- "client_compressed_streaming : compressed request streaming with "
- "client_compressed_unary : single compressed request;\n"
- "client_streaming : request streaming with single response;\n"
- "compute_engine_creds: large_unary with compute engine auth;\n"
- "custom_metadata: server will echo custom metadata;\n"
- "empty_stream : bi-di stream with no request/response;\n"
- "empty_unary : empty (zero bytes) request and response;\n"
- "half_duplex : half-duplex streaming;\n"
- "jwt_token_creds: large_unary with JWT token auth;\n"
- "large_unary : single request and (large) response;\n"
- "oauth2_auth_token: raw oauth2 access token auth;\n"
- "per_rpc_creds: raw oauth2 access token on a single rpc;\n"
- "ping_pong : full-duplex streaming;\n"
- "response streaming;\n"
- "server_compressed_streaming : single request with compressed "
- "server_compressed_unary : single compressed response;\n"
- "server_streaming : single request with response streaming;\n"
- "slow_consumer : single request with response streaming with "
- "slow client consumer;\n"
- "status_code_and_message: verify status code & message;\n"
- "timeout_on_sleeping_server: deadline exceeds on stream;\n"
- "unimplemented_method: client calls an unimplemented_method;\n");
+DEFINE_string(
+ test_case, "large_unary",
+ "Configure different test cases. Valid options are:\n\n"
+ "all : all test cases;\n"
+ "cancel_after_begin : cancel stream after starting it;\n"
+ "cancel_after_first_response: cancel on first response;\n"
+ "client_compressed_streaming : compressed request streaming with "
+ "client_compressed_unary : single compressed request;\n"
+ "client_streaming : request streaming with single response;\n"
+ "compute_engine_creds: large_unary with compute engine auth;\n"
+ "custom_metadata: server will echo custom metadata;\n"
+ "empty_stream : bi-di stream with no request/response;\n"
+ "empty_unary : empty (zero bytes) request and response;\n"
+ "half_duplex : half-duplex streaming;\n"
+ "jwt_token_creds: large_unary with JWT token auth;\n"
+ "large_unary : single request and (large) response;\n"
+ "oauth2_auth_token: raw oauth2 access token auth;\n"
+ "per_rpc_creds: raw oauth2 access token on a single rpc;\n"
+ "ping_pong : full-duplex streaming;\n"
+ "response streaming;\n"
+ "server_compressed_streaming : single request with compressed "
+ "server_compressed_unary : single compressed response;\n"
+ "server_streaming : single request with response streaming;\n"
+ "slow_consumer : single request with response streaming with "
+ "slow client consumer;\n"
+ "status_code_and_message: verify status code & message;\n"
+ "timeout_on_sleeping_server: deadline exceeds on stream;\n"
+ "unimplemented_method: client calls an unimplemented method;\n"
+ "unimplemented_service: client calls an unimplemented service;\n");
DEFINE_string(default_service_account, "",
"Email of GCE default service account");
DEFINE_string(service_account_key_file, "",
@@ -105,115 +107,78 @@ int main(int argc, char** argv) {
grpc::testing::InteropClient client(CreateChannelForTestCase(FLAGS_test_case),
true,
FLAGS_do_not_abort_on_transient_failures);
- if (FLAGS_test_case == "empty_unary") {
- client.DoEmpty();
- } else if (FLAGS_test_case == "large_unary") {
- client.DoLargeUnary();
- } else if (FLAGS_test_case == "server_compressed_unary") {
- client.DoServerCompressedUnary();
- } else if (FLAGS_test_case == "client_compressed_unary") {
- client.DoClientCompressedUnary();
- } else if (FLAGS_test_case == "client_streaming") {
- client.DoRequestStreaming();
- } else if (FLAGS_test_case == "server_streaming") {
- client.DoResponseStreaming();
- } else if (FLAGS_test_case == "server_compressed_streaming") {
- client.DoServerCompressedStreaming();
- } else if (FLAGS_test_case == "client_compressed_streaming") {
- client.DoClientCompressedStreaming();
- } else if (FLAGS_test_case == "slow_consumer") {
- client.DoResponseStreamingWithSlowConsumer();
- } else if (FLAGS_test_case == "half_duplex") {
- client.DoHalfDuplex();
- } else if (FLAGS_test_case == "ping_pong") {
- client.DoPingPong();
- } else if (FLAGS_test_case == "cancel_after_begin") {
- client.DoCancelAfterBegin();
- } else if (FLAGS_test_case == "cancel_after_first_response") {
- client.DoCancelAfterFirstResponse();
- } else if (FLAGS_test_case == "timeout_on_sleeping_server") {
- client.DoTimeoutOnSleepingServer();
- } else if (FLAGS_test_case == "empty_stream") {
- client.DoEmptyStream();
- } else if (FLAGS_test_case == "compute_engine_creds") {
- client.DoComputeEngineCreds(FLAGS_default_service_account,
- FLAGS_oauth_scope);
- } else if (FLAGS_test_case == "jwt_token_creds") {
- grpc::string json_key = GetServiceAccountJsonKey();
- client.DoJwtTokenCreds(json_key);
- } else if (FLAGS_test_case == "oauth2_auth_token") {
- client.DoOauth2AuthToken(FLAGS_default_service_account, FLAGS_oauth_scope);
- } else if (FLAGS_test_case == "per_rpc_creds") {
- grpc::string json_key = GetServiceAccountJsonKey();
- client.DoPerRpcCreds(json_key);
- } else if (FLAGS_test_case == "status_code_and_message") {
- client.DoStatusWithMessage();
- } else if (FLAGS_test_case == "custom_metadata") {
- client.DoCustomMetadata();
- } else if (FLAGS_test_case == "unimplemented_method") {
- client.DoUnimplementedMethod();
- } else if (FLAGS_test_case == "cacheable_unary") {
- client.DoCacheableUnary();
- } else if (FLAGS_test_case == "all") {
- client.DoEmpty();
- client.DoLargeUnary();
- client.DoClientCompressedUnary();
- client.DoServerCompressedUnary();
- client.DoRequestStreaming();
- client.DoResponseStreaming();
- client.DoClientCompressedStreaming();
- client.DoServerCompressedStreaming();
- client.DoHalfDuplex();
- client.DoPingPong();
- client.DoCancelAfterBegin();
- client.DoCancelAfterFirstResponse();
- client.DoTimeoutOnSleepingServer();
- client.DoEmptyStream();
- client.DoStatusWithMessage();
- client.DoCustomMetadata();
- client.DoUnimplementedMethod();
- client.DoCacheableUnary();
- // service_account_creds and jwt_token_creds can only run with ssl.
- if (FLAGS_use_tls) {
- grpc::string json_key = GetServiceAccountJsonKey();
- client.DoJwtTokenCreds(json_key);
- client.DoOauth2AuthToken(FLAGS_default_service_account,
- FLAGS_oauth_scope);
- client.DoPerRpcCreds(json_key);
+
+ std::unordered_map<grpc::string, std::function<bool()>> actions;
+ actions["empty_unary"] =
+ std::bind(&grpc::testing::InteropClient::DoEmpty, &client);
+ actions["large_unary"] =
+ std::bind(&grpc::testing::InteropClient::DoLargeUnary, &client);
+ actions["server_compressed_unary"] = std::bind(
+ &grpc::testing::InteropClient::DoServerCompressedUnary, &client);
+ actions["client_compressed_unary"] = std::bind(
+ &grpc::testing::InteropClient::DoClientCompressedUnary, &client);
+ actions["client_streaming"] =
+ std::bind(&grpc::testing::InteropClient::DoRequestStreaming, &client);
+ actions["server_streaming"] =
+ std::bind(&grpc::testing::InteropClient::DoResponseStreaming, &client);
+ actions["server_compressed_streaming"] = std::bind(
+ &grpc::testing::InteropClient::DoServerCompressedStreaming, &client);
+ actions["client_compressed_streaming"] = std::bind(
+ &grpc::testing::InteropClient::DoClientCompressedStreaming, &client);
+ actions["slow_consumer"] = std::bind(
+ &grpc::testing::InteropClient::DoResponseStreamingWithSlowConsumer,
+ &client);
+ actions["half_duplex"] =
+ std::bind(&grpc::testing::InteropClient::DoHalfDuplex, &client);
+ actions["ping_pong"] =
+ std::bind(&grpc::testing::InteropClient::DoPingPong, &client);
+ actions["cancel_after_begin"] =
+ std::bind(&grpc::testing::InteropClient::DoCancelAfterBegin, &client);
+ actions["cancel_after_first_response"] = std::bind(
+ &grpc::testing::InteropClient::DoCancelAfterFirstResponse, &client);
+ actions["timeout_on_sleeping_server"] = std::bind(
+ &grpc::testing::InteropClient::DoTimeoutOnSleepingServer, &client);
+ actions["empty_stream"] =
+ std::bind(&grpc::testing::InteropClient::DoEmptyStream, &client);
+ if (FLAGS_use_tls) {
+ actions["compute_engine_creds"] =
+ std::bind(&grpc::testing::InteropClient::DoComputeEngineCreds, &client,
+ FLAGS_default_service_account, FLAGS_oauth_scope);
+ actions["jwt_token_creds"] =
+ std::bind(&grpc::testing::InteropClient::DoJwtTokenCreds, &client,
+ GetServiceAccountJsonKey());
+ actions["oauth2_auth_token"] =
+ std::bind(&grpc::testing::InteropClient::DoOauth2AuthToken, &client,
+ FLAGS_default_service_account, FLAGS_oauth_scope);
+ actions["per_rpc_creds"] =
+ std::bind(&grpc::testing::InteropClient::DoPerRpcCreds, &client,
+ GetServiceAccountJsonKey());
+ }
+ actions["status_code_and_message"] =
+ std::bind(&grpc::testing::InteropClient::DoStatusWithMessage, &client);
+ actions["custom_metadata"] =
+ std::bind(&grpc::testing::InteropClient::DoCustomMetadata, &client);
+ actions["unimplemented_method"] =
+ std::bind(&grpc::testing::InteropClient::DoUnimplementedMethod, &client);
+ actions["unimplemented_service"] =
+ std::bind(&grpc::testing::InteropClient::DoUnimplementedService, &client);
+ // actions["cacheable_unary"] =
+ // std::bind(&grpc::testing::InteropClient::DoCacheableUnary, &client);
+
+ if (FLAGS_test_case == "all") {
+ for (const auto& action : actions) {
+ action.second();
}
- // compute_engine_creds only runs in GCE.
+ } else if (actions.find(FLAGS_test_case) != actions.end()) {
+ actions.find(FLAGS_test_case)->second();
} else {
- const char* testcases[] = {"all",
- "cacheable_unary",
- "cancel_after_begin",
- "cancel_after_first_response",
- "client_compressed_streaming",
- "client_compressed_unary",
- "client_streaming",
- "compute_engine_creds",
- "custom_metadata",
- "empty_stream",
- "empty_unary",
- "half_duplex",
- "jwt_token_creds",
- "large_unary",
- "oauth2_auth_token",
- "oauth2_auth_token",
- "per_rpc_creds",
- "per_rpc_creds",
- "ping_pong",
- "server_compressed_streaming",
- "server_compressed_unary",
- "server_streaming",
- "status_code_and_message",
- "timeout_on_sleeping_server",
- "unimplemented_method"};
- char* joined_testcases =
- gpr_strjoin_sep(testcases, GPR_ARRAY_SIZE(testcases), "\n", NULL);
-
+ grpc::string test_cases;
+ for (const auto& action : actions) {
+ if (!test_cases.empty()) test_cases += "\n";
+ test_cases += action.first;
+ }
gpr_log(GPR_ERROR, "Unsupported test case %s. Valid options are\n%s",
- FLAGS_test_case.c_str(), joined_testcases);
- gpr_free(joined_testcases);
+ FLAGS_test_case.c_str(), test_cases.c_str());
ret = 1;
}
diff --git a/test/cpp/interop/client_helper.cc b/test/cpp/interop/client_helper.cc
index c171969e14..d3192ad0c9 100644
--- a/test/cpp/interop/client_helper.cc
+++ b/test/cpp/interop/client_helper.cc
@@ -33,8 +33,6 @@
#include "test/cpp/interop/client_helper.h"
-#include <unistd.h>
-
#include <fstream>
#include <memory>
#include <sstream>
@@ -50,8 +48,10 @@
#include "src/cpp/client/secure_credentials.h"
#include "test/core/security/oauth2_utils.h"
#include "test/cpp/util/create_test_channel.h"
+#include "test/cpp/util/test_credentials_provider.h"
DECLARE_bool(use_tls);
+DECLARE_string(custom_credentials_type);
DECLARE_bool(use_test_ca);
DECLARE_int32(server_port);
DECLARE_string(server_host);
@@ -114,8 +114,12 @@ std::shared_ptr<Channel> CreateChannelForTestCase(
creds = AccessTokenCredentials(raw_token);
GPR_ASSERT(creds);
}
- return CreateTestChannel(host_port, FLAGS_server_host_override, FLAGS_use_tls,
- !FLAGS_use_test_ca, creds);
+ if (FLAGS_custom_credentials_type.empty()) {
+ return CreateTestChannel(host_port, FLAGS_server_host_override,
+ FLAGS_use_tls, !FLAGS_use_test_ca, creds);
+ } else {
+ return CreateTestChannel(host_port, FLAGS_custom_credentials_type, creds);
+ }
}
} // namespace testing
diff --git a/test/cpp/interop/http2_client.cc b/test/cpp/interop/http2_client.cc
new file mode 100644
index 0000000000..01c07823cf
--- /dev/null
+++ b/test/cpp/interop/http2_client.cc
@@ -0,0 +1,244 @@
+/*
+ *
+ * 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 <thread>
+
+#include <gflags/gflags.h>
+#include <grpc++/channel.h>
+#include <grpc++/client_context.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+
+#include "src/core/lib/transport/byte_stream.h"
+#include "src/proto/grpc/testing/messages.grpc.pb.h"
+#include "src/proto/grpc/testing/test.grpc.pb.h"
+#include "test/cpp/interop/http2_client.h"
+
+#include "src/core/lib/support/string.h"
+#include "test/cpp/util/create_test_channel.h"
+#include "test/cpp/util/test_config.h"
+
+namespace grpc {
+namespace testing {
+
+namespace {
+const int kLargeRequestSize = 271828;
+const int kLargeResponseSize = 314159;
+} // namespace
+
+Http2Client::ServiceStub::ServiceStub(std::shared_ptr<Channel> channel)
+ : channel_(channel) {
+ stub_ = TestService::NewStub(channel);
+}
+
+TestService::Stub* Http2Client::ServiceStub::Get() { return stub_.get(); }
+
+Http2Client::Http2Client(std::shared_ptr<Channel> channel)
+ : serviceStub_(channel),
+ channel_(channel),
+ defaultRequest_(BuildDefaultRequest()) {}
+
+bool Http2Client::AssertStatusCode(const Status& s, StatusCode expected_code) {
+ if (s.error_code() == expected_code) {
+ return true;
+ }
+
+ gpr_log(GPR_ERROR, "Error status code: %d (expected: %d), message: %s",
+ s.error_code(), expected_code, s.error_message().c_str());
+ abort();
+}
+
+Status Http2Client::SendUnaryCall(SimpleResponse* response) {
+ ClientContext context;
+ return serviceStub_.Get()->UnaryCall(&context, defaultRequest_, response);
+}
+
+SimpleRequest Http2Client::BuildDefaultRequest() {
+ SimpleRequest request;
+ request.set_response_size(kLargeResponseSize);
+ grpc::string payload(kLargeRequestSize, '\0');
+ request.mutable_payload()->set_body(payload.c_str(), kLargeRequestSize);
+ return request;
+}
+
+bool Http2Client::DoRstAfterHeader() {
+ gpr_log(GPR_DEBUG, "Sending RPC and expecting reset stream after header");
+
+ SimpleResponse response;
+ AssertStatusCode(SendUnaryCall(&response), grpc::StatusCode::INTERNAL);
+ GPR_ASSERT(!response.has_payload()); // no data should be received
+
+ gpr_log(GPR_DEBUG, "Done testing reset stream after header");
+ return true;
+}
+
+bool Http2Client::DoRstAfterData() {
+ gpr_log(GPR_DEBUG, "Sending RPC and expecting reset stream after data");
+
+ SimpleResponse response;
+ AssertStatusCode(SendUnaryCall(&response), grpc::StatusCode::INTERNAL);
+ GPR_ASSERT(response.has_payload()); // data should be received
+
+ gpr_log(GPR_DEBUG, "Done testing reset stream after data");
+ return true;
+}
+
+bool Http2Client::DoRstDuringData() {
+ gpr_log(GPR_DEBUG, "Sending RPC and expecting reset stream during data");
+
+ SimpleResponse response;
+ AssertStatusCode(SendUnaryCall(&response), grpc::StatusCode::INTERNAL);
+ GPR_ASSERT(!response.has_payload()); // no data should be received
+
+ gpr_log(GPR_DEBUG, "Done testing reset stream during data");
+ return true;
+}
+
+bool Http2Client::DoGoaway() {
+ gpr_log(GPR_DEBUG, "Sending two RPCs and expecting goaway");
+ SimpleResponse response;
+ AssertStatusCode(SendUnaryCall(&response), grpc::StatusCode::OK);
+ GPR_ASSERT(response.payload().body() ==
+ grpc::string(kLargeResponseSize, '\0'));
+
+ // Sleep for one second to give time for client to receive goaway frame.
+ gpr_timespec sleep_time = gpr_time_add(
+ gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(1, GPR_TIMESPAN));
+ gpr_sleep_until(sleep_time);
+
+ response.Clear();
+ AssertStatusCode(SendUnaryCall(&response), grpc::StatusCode::OK);
+ GPR_ASSERT(response.payload().body() ==
+ grpc::string(kLargeResponseSize, '\0'));
+ gpr_log(GPR_DEBUG, "Done testing goaway");
+ return true;
+}
+
+bool Http2Client::DoPing() {
+ gpr_log(GPR_DEBUG, "Sending RPC and expecting ping");
+ SimpleResponse response;
+ AssertStatusCode(SendUnaryCall(&response), grpc::StatusCode::OK);
+ GPR_ASSERT(response.payload().body() ==
+ grpc::string(kLargeResponseSize, '\0'));
+ gpr_log(GPR_DEBUG, "Done testing ping");
+ return true;
+}
+
+void Http2Client::MaxStreamsWorker(std::shared_ptr<grpc::Channel> channel) {
+ SimpleResponse response;
+ AssertStatusCode(SendUnaryCall(&response), grpc::StatusCode::OK);
+ GPR_ASSERT(response.payload().body() ==
+ grpc::string(kLargeResponseSize, '\0'));
+}
+
+bool Http2Client::DoMaxStreams() {
+ gpr_log(GPR_DEBUG, "Testing max streams");
+
+ // Make an initial call on the channel to ensure the server's max streams
+ // setting is received
+ SimpleResponse response;
+ AssertStatusCode(SendUnaryCall(&response), grpc::StatusCode::OK);
+ GPR_ASSERT(response.payload().body() ==
+ grpc::string(kLargeResponseSize, '\0'));
+
+ std::vector<std::thread> test_threads;
+
+ for (int i = 0; i < 10; i++) {
+ test_threads.emplace_back(
+ std::thread(&Http2Client::MaxStreamsWorker, this, channel_));
+ }
+
+ for (auto it = test_threads.begin(); it != test_threads.end(); it++) {
+ it->join();
+ }
+
+ gpr_log(GPR_DEBUG, "Done testing max streams");
+ return true;
+}
+
+} // namespace testing
+} // namespace grpc
+
+DEFINE_int32(server_port, 0, "Server port.");
+DEFINE_string(server_host, "localhost", "Server host to connect to");
+DEFINE_string(test_case, "rst_after_header",
+ "Configure different test cases. Valid options are:\n\n"
+ "goaway\n"
+ "max_streams\n"
+ "ping\n"
+ "rst_after_data\n"
+ "rst_after_header\n"
+ "rst_during_data\n");
+
+int main(int argc, char** argv) {
+ grpc::testing::InitTest(&argc, &argv, true);
+ GPR_ASSERT(FLAGS_server_port);
+ const int host_port_buf_size = 1024;
+ char host_port[host_port_buf_size];
+ snprintf(host_port, host_port_buf_size, "%s:%d", FLAGS_server_host.c_str(),
+ FLAGS_server_port);
+ std::shared_ptr<grpc::Channel> channel =
+ grpc::CreateTestChannel(host_port, false);
+ GPR_ASSERT(channel->WaitForConnected(gpr_time_add(
+ gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(300, GPR_TIMESPAN))));
+ grpc::testing::Http2Client client(channel);
+ gpr_log(GPR_INFO, "Testing case: %s", FLAGS_test_case.c_str());
+ int ret = 0;
+ if (FLAGS_test_case == "rst_after_header") {
+ client.DoRstAfterHeader();
+ } else if (FLAGS_test_case == "rst_after_data") {
+ client.DoRstAfterData();
+ } else if (FLAGS_test_case == "rst_during_data") {
+ client.DoRstDuringData();
+ } else if (FLAGS_test_case == "goaway") {
+ client.DoGoaway();
+ } else if (FLAGS_test_case == "ping") {
+ client.DoPing();
+ } else if (FLAGS_test_case == "max_streams") {
+ client.DoMaxStreams();
+ } else {
+ const char* testcases[] = {
+ "goaway", "max_streams", "ping",
+ "rst_after_data", "rst_after_header", "rst_during_data"};
+ char* joined_testcases =
+ gpr_strjoin_sep(testcases, GPR_ARRAY_SIZE(testcases), "\n", NULL);
+
+ gpr_log(GPR_ERROR, "Unsupported test case %s. Valid options are\n%s",
+ FLAGS_test_case.c_str(), joined_testcases);
+ gpr_free(joined_testcases);
+ ret = 1;
+ }
+
+ return ret;
+}
diff --git a/test/cpp/qps/limit_cores.cc b/test/cpp/interop/http2_client.h
index b5c222542b..12df5d26bc 100644
--- a/test/cpp/qps/limit_cores.cc
+++ b/test/cpp/interop/http2_client.h
@@ -31,57 +31,53 @@
*
*/
-#include "test/cpp/qps/limit_cores.h"
+#ifndef GRPC_TEST_CPP_INTEROP_HTTP2_CLIENT_H
+#define GRPC_TEST_CPP_INTEROP_HTTP2_CLIENT_H
-#include <grpc/support/cpu.h>
-#include <grpc/support/log.h>
-#include <grpc/support/port_platform.h>
+#include <memory>
-#ifdef GPR_CPU_LINUX
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#include <sched.h>
+#include <grpc++/channel.h>
+#include <grpc/grpc.h>
+#include "src/proto/grpc/testing/messages.grpc.pb.h"
+#include "src/proto/grpc/testing/test.grpc.pb.h"
namespace grpc {
namespace testing {
-int LimitCores(const int* cores, int cores_size) {
- const int num_cores = gpr_cpu_num_cores();
- int cores_set = 0;
+class Http2Client {
+ public:
+ explicit Http2Client(std::shared_ptr<Channel> channel);
+ ~Http2Client() {}
- cpu_set_t* cpup = CPU_ALLOC(num_cores);
- GPR_ASSERT(cpup);
- const size_t size = CPU_ALLOC_SIZE(num_cores);
- CPU_ZERO_S(size, cpup);
+ bool DoRstAfterHeader();
+ bool DoRstAfterData();
+ bool DoRstDuringData();
+ bool DoGoaway();
+ bool DoPing();
+ bool DoMaxStreams();
- if (cores_size > 0) {
- for (int i = 0; i < cores_size; i++) {
- if (cores[i] < num_cores) {
- CPU_SET_S(cores[i], size, cpup);
- cores_set++;
- }
- }
- } else {
- for (int i = 0; i < num_cores; i++) {
- CPU_SET_S(i, size, cpup);
- cores_set++;
- }
- }
- bool affinity_set = (sched_setaffinity(0, size, cpup) == 0);
- CPU_FREE(cpup);
- return affinity_set ? cores_set : num_cores;
-}
+ private:
+ class ServiceStub {
+ public:
+ ServiceStub(std::shared_ptr<Channel> channel);
-} // namespace testing
-} // namespace grpc
-#else
-namespace grpc {
-namespace testing {
+ TestService::Stub* Get();
+
+ private:
+ std::unique_ptr<TestService::Stub> stub_;
+ std::shared_ptr<Channel> channel_;
+ };
-// LimitCores is not currently supported for non-Linux platforms
-int LimitCores(const int*, int) { return gpr_cpu_num_cores(); }
+ void MaxStreamsWorker(std::shared_ptr<grpc::Channel> channel);
+ bool AssertStatusCode(const Status& s, StatusCode expected_code);
+ Status SendUnaryCall(SimpleResponse* response);
+ SimpleRequest BuildDefaultRequest();
+ ServiceStub serviceStub_;
+ std::shared_ptr<Channel> channel_;
+ SimpleRequest defaultRequest_;
+};
} // namespace testing
} // namespace grpc
-#endif
+
+#endif // GRPC_TEST_CPP_INTEROP_HTTP2_CLIENT_H
diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc
index ffd19eb1d5..b7f2723c39 100644
--- a/test/cpp/interop/interop_client.cc
+++ b/test/cpp/interop/interop_client.cc
@@ -31,7 +31,6 @@
*
*/
-#include <unistd.h>
#include <cinttypes>
#include <fstream>
#include <memory>
@@ -43,6 +42,7 @@
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
+#include <grpc/support/time.h>
#include <grpc/support/useful.h>
#include "src/core/lib/transport/byte_stream.h"
@@ -107,6 +107,14 @@ TestService::Stub* InteropClient::ServiceStub::Get() {
return stub_.get();
}
+UnimplementedService::Stub*
+InteropClient::ServiceStub::GetUnimplementedServiceStub() {
+ if (unimplemented_service_stub_ == nullptr) {
+ unimplemented_service_stub_ = UnimplementedService::NewStub(channel_);
+ }
+ return unimplemented_service_stub_.get();
+}
+
void InteropClient::ServiceStub::Reset(std::shared_ptr<Channel> channel) {
channel_ = channel;
@@ -162,8 +170,8 @@ bool InteropClient::AssertStatusCode(const Status& s,
bool InteropClient::DoEmpty() {
gpr_log(GPR_DEBUG, "Sending an empty rpc...");
- Empty request = Empty::default_instance();
- Empty response = Empty::default_instance();
+ Empty request;
+ Empty response;
ClientContext context;
Status s = serviceStub_.Get()->EmptyCall(&context, request, &response);
@@ -610,7 +618,9 @@ bool InteropClient::DoResponseStreamingWithSlowConsumer() {
GPR_ASSERT(response.payload().body() ==
grpc::string(kResponseMessageSize, '\0'));
gpr_log(GPR_DEBUG, "received message %d", i);
- usleep(kReceiveDelayMilliSeconds * 1000);
+ gpr_sleep_until(gpr_time_add(
+ gpr_now(GPR_CLOCK_REALTIME),
+ gpr_time_from_millis(kReceiveDelayMilliSeconds, GPR_TIMESPAN)));
++i;
}
@@ -827,21 +837,42 @@ bool InteropClient::DoStatusWithMessage() {
gpr_log(GPR_DEBUG,
"Sending RPC with a request for status code 2 and message");
+ const grpc::StatusCode test_code = grpc::StatusCode::UNKNOWN;
+ const grpc::string test_msg = "This is a test message";
+
+ // Test UnaryCall.
ClientContext context;
SimpleRequest request;
SimpleResponse response;
EchoStatus* requested_status = request.mutable_response_status();
- requested_status->set_code(grpc::StatusCode::UNKNOWN);
- grpc::string test_msg = "This is a test message";
+ requested_status->set_code(test_code);
requested_status->set_message(test_msg);
-
Status s = serviceStub_.Get()->UnaryCall(&context, request, &response);
-
if (!AssertStatusCode(s, grpc::StatusCode::UNKNOWN)) {
return false;
}
+ GPR_ASSERT(s.error_message() == test_msg);
+ // Test FullDuplexCall.
+ ClientContext stream_context;
+ std::shared_ptr<ClientReaderWriter<StreamingOutputCallRequest,
+ StreamingOutputCallResponse>>
+ stream(serviceStub_.Get()->FullDuplexCall(&stream_context));
+ StreamingOutputCallRequest streaming_request;
+ requested_status = streaming_request.mutable_response_status();
+ requested_status->set_code(test_code);
+ requested_status->set_message(test_msg);
+ stream->Write(streaming_request);
+ stream->WritesDone();
+ StreamingOutputCallResponse streaming_response;
+ while (stream->Read(&streaming_response))
+ ;
+ s = stream->Finish();
+ if (!AssertStatusCode(s, grpc::StatusCode::UNKNOWN)) {
+ return false;
+ }
GPR_ASSERT(s.error_message() == test_msg);
+
gpr_log(GPR_DEBUG, "Done testing Status and Message");
return true;
}
@@ -917,7 +948,7 @@ bool InteropClient::DoCustomMetadata() {
const auto& server_initial_metadata = context.GetServerInitialMetadata();
auto iter = server_initial_metadata.find(kEchoInitialMetadataKey);
GPR_ASSERT(iter != server_initial_metadata.end());
- GPR_ASSERT(iter->second.data() == kInitialMetadataValue);
+ GPR_ASSERT(iter->second == kInitialMetadataValue);
const auto& server_trailing_metadata = context.GetServerTrailingMetadata();
iter = server_trailing_metadata.find(kEchoTrailingBinMetadataKey);
GPR_ASSERT(iter != server_trailing_metadata.end());
@@ -968,7 +999,7 @@ bool InteropClient::DoCustomMetadata() {
const auto& server_initial_metadata = context.GetServerInitialMetadata();
auto iter = server_initial_metadata.find(kEchoInitialMetadataKey);
GPR_ASSERT(iter != server_initial_metadata.end());
- GPR_ASSERT(iter->second.data() == kInitialMetadataValue);
+ GPR_ASSERT(iter->second == kInitialMetadataValue);
const auto& server_trailing_metadata = context.GetServerTrailingMetadata();
iter = server_trailing_metadata.find(kEchoTrailingBinMetadataKey);
GPR_ASSERT(iter != server_trailing_metadata.end());
@@ -981,15 +1012,34 @@ bool InteropClient::DoCustomMetadata() {
return true;
}
+bool InteropClient::DoUnimplementedService() {
+ gpr_log(GPR_DEBUG, "Sending a request for an unimplemented service...");
+
+ Empty request;
+ Empty response;
+ ClientContext context;
+
+ UnimplementedService::Stub* stub = serviceStub_.GetUnimplementedServiceStub();
+
+ Status s = stub->UnimplementedCall(&context, request, &response);
+
+ if (!AssertStatusCode(s, StatusCode::UNIMPLEMENTED)) {
+ return false;
+ }
+
+ gpr_log(GPR_DEBUG, "unimplemented service done.");
+ return true;
+}
+
bool InteropClient::DoUnimplementedMethod() {
gpr_log(GPR_DEBUG, "Sending a request for an unimplemented rpc...");
- Empty request = Empty::default_instance();
- Empty response = Empty::default_instance();
+ Empty request;
+ Empty response;
ClientContext context;
Status s =
- serviceStub_.Get()->UnimplementedMethod(&context, request, &response);
+ serviceStub_.Get()->UnimplementedCall(&context, request, &response);
if (!AssertStatusCode(s, StatusCode::UNIMPLEMENTED)) {
return false;
diff --git a/test/cpp/interop/interop_client.h b/test/cpp/interop/interop_client.h
index 0a96e7734d..74f4db6b78 100644
--- a/test/cpp/interop/interop_client.h
+++ b/test/cpp/interop/interop_client.h
@@ -80,6 +80,7 @@ class InteropClient {
bool DoStatusWithMessage();
bool DoCustomMetadata();
bool DoUnimplementedMethod();
+ bool DoUnimplementedService();
bool DoCacheableUnary();
// Auth tests.
// username is a string containing the user email
@@ -100,11 +101,13 @@ class InteropClient {
ServiceStub(std::shared_ptr<Channel> channel, bool new_stub_every_call);
TestService::Stub* Get();
+ UnimplementedService::Stub* GetUnimplementedServiceStub();
void Reset(std::shared_ptr<Channel> channel);
private:
std::unique_ptr<TestService::Stub> stub_;
+ std::unique_ptr<UnimplementedService::Stub> unimplemented_service_stub_;
std::shared_ptr<Channel> channel_;
bool new_stub_every_call_; // If true, a new stub is returned by every
// Get() call
diff --git a/test/cpp/interop/interop_server.cc b/test/cpp/interop/interop_server.cc
index 58f20aa611..5a810b45ef 100644
--- a/test/cpp/interop/interop_server.cc
+++ b/test/cpp/interop/interop_server.cc
@@ -31,8 +31,6 @@
*
*/
-#include <unistd.h>
-
#include <fstream>
#include <memory>
#include <sstream>
@@ -45,6 +43,7 @@
#include <grpc++/server_context.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
+#include <grpc/support/time.h>
#include <grpc/support/useful.h>
#include "src/core/lib/support/string.h"
@@ -56,6 +55,7 @@
#include "test/cpp/util/test_config.h"
DEFINE_bool(use_tls, false, "Whether to use tls.");
+DEFINE_string(custom_credentials_type, "", "User provided credentials type.");
DEFINE_int32(port, 0, "Server port.");
DEFINE_int32(max_send_message_size, -1, "The maximum send message size.");
@@ -90,7 +90,9 @@ void MaybeEchoMetadata(ServerContext* context) {
auto iter = client_metadata.find(kEchoInitialMetadataKey);
if (iter != client_metadata.end()) {
- context->AddInitialMetadata(kEchoInitialMetadataKey, iter->second.data());
+ context->AddInitialMetadata(
+ kEchoInitialMetadataKey,
+ grpc::string(iter->second.begin(), iter->second.end()));
}
iter = client_metadata.find(kEchoTrailingBinMetadataKey);
if (iter != client_metadata.end()) {
@@ -104,7 +106,9 @@ void MaybeEchoMetadata(ServerContext* context) {
if (iter != client_metadata.end()) {
iter = client_metadata.find("user-agent");
if (iter != client_metadata.end()) {
- context->AddInitialMetadata(kEchoUserAgentKey, iter->second.data());
+ context->AddInitialMetadata(
+ kEchoUserAgentKey,
+ grpc::string(iter->second.begin(), iter->second.end()));
}
}
}
@@ -269,6 +273,11 @@ class TestServiceImpl : public TestService::Service {
StreamingOutputCallResponse response;
bool write_success = true;
while (write_success && stream->Read(&request)) {
+ if (request.has_response_status()) {
+ return Status(
+ static_cast<grpc::StatusCode>(request.response_status().code()),
+ request.response_status().message());
+ }
if (request.response_parameters_size() != 0) {
response.mutable_payload()->set_type(request.payload().type());
response.mutable_payload()->set_body(
@@ -339,7 +348,8 @@ void grpc::testing::interop::RunServer(
}
std::unique_ptr<Server> server(builder.BuildAndStart());
gpr_log(GPR_INFO, "Server listening on %s", server_address.str().c_str());
- while (!g_got_sigint) {
- sleep(5);
+ while (!gpr_atm_no_barrier_load(&g_got_sigint)) {
+ gpr_sleep_until(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+ gpr_time_from_seconds(5, GPR_TIMESPAN)));
}
}
diff --git a/test/cpp/interop/interop_server_bootstrap.cc b/test/cpp/interop/interop_server_bootstrap.cc
index 424f7ca7f0..7cbf221a03 100644
--- a/test/cpp/interop/interop_server_bootstrap.cc
+++ b/test/cpp/interop/interop_server_bootstrap.cc
@@ -32,15 +32,14 @@
*/
#include <signal.h>
-#include <unistd.h>
#include "test/cpp/interop/server_helper.h"
#include "test/cpp/util/test_config.h"
-bool grpc::testing::interop::g_got_sigint = false;
+gpr_atm grpc::testing::interop::g_got_sigint;
static void sigint_handler(int x) {
- grpc::testing::interop::g_got_sigint = true;
+ gpr_atm_no_barrier_store(&grpc::testing::interop::g_got_sigint, true);
}
int main(int argc, char** argv) {
diff --git a/test/cpp/interop/interop_test.cc b/test/cpp/interop/interop_test.cc
index 8e71a2bb29..d4004740a4 100644
--- a/test/cpp/interop/interop_test.cc
+++ b/test/cpp/interop/interop_test.cc
@@ -44,17 +44,21 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <gflags/gflags.h>
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "test/core/util/port.h"
+#include "test/cpp/util/test_config.h"
extern "C" {
#include "src/core/lib/iomgr/socket_utils_posix.h"
#include "src/core/lib/support/string.h"
}
+DEFINE_string(extra_server_flags, "", "Extra flags to pass to server.");
+
int test_client(const char* root, const char* host, int port) {
int status;
pid_t cli;
@@ -80,6 +84,7 @@ int test_client(const char* root, const char* host, int port) {
}
int main(int argc, char** argv) {
+ grpc::testing::InitTest(&argc, &argv, true);
char* me = argv[0];
char* lslash = strrchr(me, '/');
char root[1024];
@@ -105,19 +110,23 @@ int main(int argc, char** argv) {
/* start the server */
svr = fork();
if (svr == 0) {
- char* binary_path;
- char* port_arg;
- gpr_asprintf(&binary_path, "%s/interop_server", root);
- gpr_asprintf(&port_arg, "--port=%d", port);
-
- execl(binary_path, binary_path, port_arg, NULL);
-
- gpr_free(binary_path);
- gpr_free(port_arg);
+ const size_t num_args = 3 + !FLAGS_extra_server_flags.empty();
+ char** args = (char**)gpr_malloc(sizeof(char*) * num_args);
+ memset(args, 0, sizeof(char*) * num_args);
+ gpr_asprintf(&args[0], "%s/interop_server", root);
+ gpr_asprintf(&args[1], "--port=%d", port);
+ if (!FLAGS_extra_server_flags.empty()) {
+ args[2] = gpr_strdup(FLAGS_extra_server_flags.c_str());
+ }
+ execv(args[0], args);
+ for (size_t i = 0; i < num_args - 1; ++i) {
+ gpr_free(args[i]);
+ }
+ gpr_free(args);
return 1;
}
/* wait a little */
- sleep(2);
+ sleep(10);
/* start the clients */
ret = test_client(root, "127.0.0.1", port);
if (ret != 0) return ret;
diff --git a/test/cpp/interop/reconnect_interop_client.cc b/test/cpp/interop/reconnect_interop_client.cc
index 797e52c744..1c2f606637 100644
--- a/test/cpp/interop/reconnect_interop_client.cc
+++ b/test/cpp/interop/reconnect_interop_client.cc
@@ -48,7 +48,7 @@
DEFINE_int32(server_control_port, 0, "Server port for control rpcs.");
DEFINE_int32(server_retry_port, 0, "Server port for testing reconnection.");
-DEFINE_string(server_host, "127.0.0.1", "Server host to connect to");
+DEFINE_string(server_host, "localhost", "Server host to connect to");
DEFINE_int32(max_reconnect_backoff_ms, 0,
"Maximum backoff time, or 0 for default.");
diff --git a/test/cpp/interop/reconnect_interop_server.cc b/test/cpp/interop/reconnect_interop_server.cc
index 53d51e80e7..634d0a90fc 100644
--- a/test/cpp/interop/reconnect_interop_server.cc
+++ b/test/cpp/interop/reconnect_interop_server.cc
@@ -34,7 +34,6 @@
// Test description at doc/connection-backoff-interop-test-description.md
#include <signal.h>
-#include <unistd.h>
#include <condition_variable>
#include <memory>
diff --git a/test/cpp/interop/server_helper.cc b/test/cpp/interop/server_helper.cc
index 8b0b511bcb..d395f50fa5 100644
--- a/test/cpp/interop/server_helper.cc
+++ b/test/cpp/interop/server_helper.cc
@@ -39,23 +39,23 @@
#include <grpc++/security/server_credentials.h>
#include "src/core/lib/surface/call_test_only.h"
-#include "test/core/end2end/data/ssl_test_data.h"
+#include "test/cpp/util/test_credentials_provider.h"
DECLARE_bool(use_tls);
+DECLARE_string(custom_credentials_type);
namespace grpc {
namespace testing {
std::shared_ptr<ServerCredentials> CreateInteropServerCredentials() {
- if (FLAGS_use_tls) {
- SslServerCredentialsOptions::PemKeyCertPair pkcp = {test_server1_key,
- test_server1_cert};
- SslServerCredentialsOptions ssl_opts;
- ssl_opts.pem_root_certs = "";
- ssl_opts.pem_key_cert_pairs.push_back(pkcp);
- return SslServerCredentials(ssl_opts);
+ if (!FLAGS_custom_credentials_type.empty()) {
+ return GetCredentialsProvider()->GetServerCredentials(
+ FLAGS_custom_credentials_type);
+ } else if (FLAGS_use_tls) {
+ return GetCredentialsProvider()->GetServerCredentials(kTlsCredentialsType);
} else {
- return InsecureServerCredentials();
+ return GetCredentialsProvider()->GetServerCredentials(
+ kInsecureCredentialsType);
}
}
diff --git a/test/cpp/interop/server_helper.h b/test/cpp/interop/server_helper.h
index fc4ea8b3e8..99539adee5 100644
--- a/test/cpp/interop/server_helper.h
+++ b/test/cpp/interop/server_helper.h
@@ -36,9 +36,11 @@
#include <memory>
+#include <grpc/compression.h>
+#include <grpc/impl/codegen/atm.h>
+
#include <grpc++/security/server_credentials.h>
#include <grpc++/server_context.h>
-#include <grpc/compression.h>
namespace grpc {
namespace testing {
@@ -62,7 +64,7 @@ class InteropServerContextInspector {
namespace interop {
-extern bool g_got_sigint;
+extern gpr_atm g_got_sigint;
void RunServer(std::shared_ptr<ServerCredentials> creds);
} // namespace interop
diff --git a/test/cpp/interop/stress_test.cc b/test/cpp/interop/stress_test.cc
index 5647cb5531..fdc0a613f3 100644
--- a/test/cpp/interop/stress_test.cc
+++ b/test/cpp/interop/stress_test.cc
@@ -40,7 +40,6 @@
#include <gflags/gflags.h>
#include <grpc++/create_channel.h>
#include <grpc++/grpc++.h>
-#include <grpc++/impl/thd.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
@@ -48,6 +47,7 @@
#include "src/proto/grpc/testing/metrics.pb.h"
#include "test/cpp/interop/interop_client.h"
#include "test/cpp/interop/stress_interop_client.h"
+#include "test/cpp/util/create_test_channel.h"
#include "test/cpp/util/metrics_server.h"
#include "test/cpp/util/test_config.h"
@@ -67,8 +67,7 @@ DEFINE_int32(test_duration_secs, -1,
" forcefully terminated.");
DEFINE_string(server_addresses, "localhost:8080",
- "The list of server"
- " addresses in the format:\n"
+ "The list of server addresses. The format is: \n"
" \"<name_1>:<port_1>,<name_2>:<port_1>...<name_N>:<port_N>\"\n"
" Note: <name> can be servername or IP address.");
@@ -115,6 +114,13 @@ DEFINE_bool(do_not_abort_on_transient_failures, true,
"If set to 'true', abort() is not called in case of transient "
"failures like temporary connection failures.");
+// Options from client.cc (for compatibility with interop test).
+// TODO(sreek): Consolidate overlapping options
+DEFINE_bool(use_tls, false, "Whether to use tls.");
+DEFINE_bool(use_test_ca, false, "False to use SSL roots for google");
+DEFINE_string(server_host_override, "foo.test.google.fr",
+ "Override the server host which is sent in HTTP header");
+
using grpc::testing::kTestCaseList;
using grpc::testing::MetricsService;
using grpc::testing::MetricsServiceImpl;
@@ -247,7 +253,7 @@ int main(int argc, char** argv) {
// Parse test cases and weights
if (FLAGS_test_cases.length() == 0) {
- gpr_log(GPR_ERROR, "Not running tests. The 'test_cases' string is empty");
+ gpr_log(GPR_ERROR, "No test cases supplied");
return 1;
}
@@ -265,7 +271,7 @@ int main(int argc, char** argv) {
gpr_log(GPR_INFO, "Starting test(s)..");
- std::vector<grpc::thread> test_threads;
+ std::vector<std::thread> test_threads;
// Create and start the test threads.
// Note that:
@@ -283,9 +289,10 @@ int main(int argc, char** argv) {
// Create channel(s) for each server
for (int channel_idx = 0; channel_idx < FLAGS_num_channels_per_server;
channel_idx++) {
- // TODO (sreek). This won't work for tests that require Authentication
- std::shared_ptr<grpc::Channel> channel(
- grpc::CreateChannel(*it, grpc::InsecureChannelCredentials()));
+ gpr_log(GPR_INFO, "Starting test with %s channel_idx=%d..", it->c_str(),
+ channel_idx);
+ std::shared_ptr<grpc::Channel> channel = grpc::CreateTestChannel(
+ *it, FLAGS_server_host_override, FLAGS_use_tls, !FLAGS_use_test_ca);
// Create stub(s) for each channel
for (int stub_idx = 0; stub_idx < FLAGS_num_stubs_per_channel;
@@ -300,7 +307,7 @@ int main(int argc, char** argv) {
"/stress_test/server_%d/channel_%d/stub_%d/qps",
server_idx, channel_idx, stub_idx);
- test_threads.emplace_back(grpc::thread(
+ test_threads.emplace_back(std::thread(
&StressTestInteropClient::MainLoop, client,
metrics_service.CreateQpsGauge(buffer, &is_already_created)));
@@ -311,8 +318,10 @@ int main(int argc, char** argv) {
}
// Start metrics server before waiting for the stress test threads
- std::unique_ptr<grpc::Server> metrics_server =
- metrics_service.StartServer(FLAGS_metrics_port);
+ std::unique_ptr<grpc::Server> metrics_server;
+ if (FLAGS_metrics_port > 0) {
+ metrics_server = metrics_service.StartServer(FLAGS_metrics_port);
+ }
// Wait for the stress test threads to complete
for (auto it = test_threads.begin(); it != test_threads.end(); it++) {
diff --git a/test/cpp/microbenchmarks/bm_call_create.cc b/test/cpp/microbenchmarks/bm_call_create.cc
new file mode 100644
index 0000000000..e2e5bbbe00
--- /dev/null
+++ b/test/cpp/microbenchmarks/bm_call_create.cc
@@ -0,0 +1,409 @@
+/*
+ *
+ * Copyright 2017, 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.
+ *
+ */
+
+/* This benchmark exists to ensure that the benchmark integration is
+ * working */
+
+#include <string.h>
+#include <sstream>
+
+#include <grpc++/support/channel_arguments.h>
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/string_util.h>
+
+extern "C" {
+#include "src/core/ext/client_channel/client_channel.h"
+#include "src/core/ext/load_reporting/load_reporting_filter.h"
+#include "src/core/lib/channel/channel_stack.h"
+#include "src/core/lib/channel/compress_filter.h"
+#include "src/core/lib/channel/connected_channel.h"
+#include "src/core/lib/channel/deadline_filter.h"
+#include "src/core/lib/channel/http_client_filter.h"
+#include "src/core/lib/channel/http_server_filter.h"
+#include "src/core/lib/channel/message_size_filter.h"
+#include "src/core/lib/transport/transport_impl.h"
+}
+
+#include "third_party/benchmark/include/benchmark/benchmark.h"
+
+static struct Init {
+ Init() { grpc_init(); }
+ ~Init() { grpc_shutdown(); }
+} g_init;
+
+class BaseChannelFixture {
+ public:
+ BaseChannelFixture(grpc_channel *channel) : channel_(channel) {}
+ ~BaseChannelFixture() { grpc_channel_destroy(channel_); }
+
+ grpc_channel *channel() const { return channel_; }
+
+ private:
+ grpc_channel *const channel_;
+};
+
+class InsecureChannel : public BaseChannelFixture {
+ public:
+ InsecureChannel()
+ : BaseChannelFixture(
+ grpc_insecure_channel_create("localhost:1234", NULL, NULL)) {}
+};
+
+class LameChannel : public BaseChannelFixture {
+ public:
+ LameChannel()
+ : BaseChannelFixture(grpc_lame_client_channel_create(
+ "localhost:1234", GRPC_STATUS_UNAUTHENTICATED, "blah")) {}
+};
+
+template <class Fixture>
+static void BM_CallCreateDestroy(benchmark::State &state) {
+ Fixture fixture;
+ grpc_completion_queue *cq = grpc_completion_queue_create(NULL);
+ gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+ void *method_hdl =
+ grpc_channel_register_call(fixture.channel(), "/foo/bar", NULL, NULL);
+ while (state.KeepRunning()) {
+ grpc_call_destroy(grpc_channel_create_registered_call(
+ fixture.channel(), NULL, GRPC_PROPAGATE_DEFAULTS, cq, method_hdl,
+ deadline, NULL));
+ }
+ grpc_completion_queue_destroy(cq);
+}
+
+BENCHMARK_TEMPLATE(BM_CallCreateDestroy, InsecureChannel);
+BENCHMARK_TEMPLATE(BM_CallCreateDestroy, LameChannel);
+
+static void FilterDestroy(grpc_exec_ctx *exec_ctx, void *arg,
+ grpc_error *error) {
+ gpr_free(arg);
+}
+
+static void DoNothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {}
+
+class FakeClientChannelFactory : public grpc_client_channel_factory {
+ public:
+ FakeClientChannelFactory() { vtable = &vtable_; }
+
+ private:
+ static void NoRef(grpc_client_channel_factory *factory) {}
+ static void NoUnref(grpc_exec_ctx *exec_ctx,
+ grpc_client_channel_factory *factory) {}
+ static grpc_subchannel *CreateSubchannel(grpc_exec_ctx *exec_ctx,
+ grpc_client_channel_factory *factory,
+ const grpc_subchannel_args *args) {
+ return nullptr;
+ }
+ static grpc_channel *CreateClientChannel(grpc_exec_ctx *exec_ctx,
+ grpc_client_channel_factory *factory,
+ const char *target,
+ grpc_client_channel_type type,
+ const grpc_channel_args *args) {
+ return nullptr;
+ }
+
+ static const grpc_client_channel_factory_vtable vtable_;
+};
+
+const grpc_client_channel_factory_vtable FakeClientChannelFactory::vtable_ = {
+ NoRef, NoUnref, CreateSubchannel, CreateClientChannel};
+
+static grpc_arg StringArg(const char *key, const char *value) {
+ grpc_arg a;
+ a.type = GRPC_ARG_STRING;
+ a.key = const_cast<char *>(key);
+ a.value.string = const_cast<char *>(value);
+ return a;
+}
+
+enum FixtureFlags : uint32_t {
+ CHECKS_NOT_LAST = 1,
+ REQUIRES_TRANSPORT = 2,
+};
+
+template <const grpc_channel_filter *kFilter, uint32_t kFlags>
+struct Fixture {
+ const grpc_channel_filter *filter = kFilter;
+ const uint32_t flags = kFlags;
+};
+
+namespace dummy_filter {
+
+static void StartTransportStreamOp(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *elem,
+ grpc_transport_stream_op *op) {}
+
+static void StartTransportOp(grpc_exec_ctx *exec_ctx,
+ grpc_channel_element *elem,
+ grpc_transport_op *op) {}
+
+static grpc_error *InitCallElem(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *elem,
+ const grpc_call_element_args *args) {
+ return GRPC_ERROR_NONE;
+}
+
+static void SetPollsetOrPollsetSet(grpc_exec_ctx *exec_ctx,
+ grpc_call_element *elem,
+ grpc_polling_entity *pollent) {}
+
+static void DestroyCallElem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
+ const grpc_call_final_info *final_info,
+ void *and_free_memory) {}
+
+grpc_error *InitChannelElem(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem,
+ grpc_channel_element_args *args) {
+ return GRPC_ERROR_NONE;
+}
+
+void DestroyChannelElem(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem) {}
+
+char *GetPeer(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) {
+ return gpr_strdup("peer");
+}
+
+void GetChannelInfo(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem,
+ const grpc_channel_info *channel_info) {}
+
+static const grpc_channel_filter dummy_filter = {StartTransportStreamOp,
+ StartTransportOp,
+ 0,
+ InitCallElem,
+ SetPollsetOrPollsetSet,
+ DestroyCallElem,
+ 0,
+ InitChannelElem,
+ DestroyChannelElem,
+ GetPeer,
+ GetChannelInfo,
+ "dummy_filter"};
+
+} // namespace dummy_filter
+
+namespace dummy_transport {
+
+/* Memory required for a single stream element - this is allocated by upper
+ layers and initialized by the transport */
+size_t sizeof_stream; /* = sizeof(transport stream) */
+
+/* name of this transport implementation */
+const char *name;
+
+/* implementation of grpc_transport_init_stream */
+int InitStream(grpc_exec_ctx *exec_ctx, grpc_transport *self,
+ grpc_stream *stream, grpc_stream_refcount *refcount,
+ const void *server_data) {
+ return 0;
+}
+
+/* implementation of grpc_transport_set_pollset */
+void SetPollset(grpc_exec_ctx *exec_ctx, grpc_transport *self,
+ grpc_stream *stream, grpc_pollset *pollset) {}
+
+/* implementation of grpc_transport_set_pollset */
+void SetPollsetSet(grpc_exec_ctx *exec_ctx, grpc_transport *self,
+ grpc_stream *stream, grpc_pollset_set *pollset_set) {}
+
+/* implementation of grpc_transport_perform_stream_op */
+void PerformStreamOp(grpc_exec_ctx *exec_ctx, grpc_transport *self,
+ grpc_stream *stream, grpc_transport_stream_op *op) {
+ grpc_closure_sched(exec_ctx, op->on_complete, GRPC_ERROR_NONE);
+}
+
+/* implementation of grpc_transport_perform_op */
+void PerformOp(grpc_exec_ctx *exec_ctx, grpc_transport *self,
+ grpc_transport_op *op) {}
+
+/* implementation of grpc_transport_destroy_stream */
+void DestroyStream(grpc_exec_ctx *exec_ctx, grpc_transport *self,
+ grpc_stream *stream, void *and_free_memory) {}
+
+/* implementation of grpc_transport_destroy */
+void Destroy(grpc_exec_ctx *exec_ctx, grpc_transport *self) {}
+
+/* implementation of grpc_transport_get_peer */
+char *GetPeer(grpc_exec_ctx *exec_ctx, grpc_transport *self) {
+ return gpr_strdup("transport_peer");
+}
+
+/* implementation of grpc_transport_get_endpoint */
+grpc_endpoint *GetEndpoint(grpc_exec_ctx *exec_ctx, grpc_transport *self) {
+ return nullptr;
+}
+
+static const grpc_transport_vtable dummy_transport_vtable = {
+ 0, "dummy_http2", InitStream,
+ SetPollset, SetPollsetSet, PerformStreamOp,
+ PerformOp, DestroyStream, Destroy,
+ GetPeer, GetEndpoint};
+
+static grpc_transport dummy_transport = {&dummy_transport_vtable};
+
+} // namespace dummy_transport
+
+class NoOp {
+ public:
+ class Op {
+ public:
+ Op(grpc_exec_ctx *exec_ctx, NoOp *p, grpc_call_stack *s) {}
+ void Finish(grpc_exec_ctx *exec_ctx) {}
+ };
+};
+
+class SendEmptyMetadata {
+ public:
+ SendEmptyMetadata() {
+ memset(&op_, 0, sizeof(op_));
+ op_.on_complete = grpc_closure_init(&closure_, DoNothing, nullptr,
+ grpc_schedule_on_exec_ctx);
+ }
+
+ class Op {
+ public:
+ Op(grpc_exec_ctx *exec_ctx, SendEmptyMetadata *p, grpc_call_stack *s) {
+ grpc_metadata_batch_init(&batch_);
+ p->op_.send_initial_metadata = &batch_;
+ }
+ void Finish(grpc_exec_ctx *exec_ctx) {
+ grpc_metadata_batch_destroy(exec_ctx, &batch_);
+ }
+
+ private:
+ grpc_metadata_batch batch_;
+ };
+
+ private:
+ const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+ const gpr_timespec start_time_ = gpr_now(GPR_CLOCK_MONOTONIC);
+ const grpc_slice method_ = grpc_slice_from_static_string("/foo/bar");
+ grpc_transport_stream_op op_;
+ grpc_closure closure_;
+};
+
+// Test a filter in isolation. Fixture specifies the filter under test (use the
+// Fixture<> template to specify this), and TestOp defines some unit of work to
+// perform on said filter.
+template <class Fixture, class TestOp>
+static void BM_IsolatedFilter(benchmark::State &state) {
+ Fixture fixture;
+ std::ostringstream label;
+
+ std::vector<grpc_arg> args;
+ FakeClientChannelFactory fake_client_channel_factory;
+ args.push_back(grpc_client_channel_factory_create_channel_arg(
+ &fake_client_channel_factory));
+ args.push_back(StringArg(GRPC_ARG_SERVER_URI, "localhost"));
+
+ grpc_channel_args channel_args = {args.size(), &args[0]};
+
+ std::vector<const grpc_channel_filter *> filters;
+ if (fixture.filter != nullptr) {
+ filters.push_back(fixture.filter);
+ }
+ if (fixture.flags & CHECKS_NOT_LAST) {
+ filters.push_back(&dummy_filter::dummy_filter);
+ label << " #has_dummy_filter";
+ }
+
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ size_t channel_size = grpc_channel_stack_size(&filters[0], filters.size());
+ grpc_channel_stack *channel_stack =
+ static_cast<grpc_channel_stack *>(gpr_zalloc(channel_size));
+ GPR_ASSERT(GRPC_LOG_IF_ERROR(
+ "call_stack_init",
+ grpc_channel_stack_init(&exec_ctx, 1, FilterDestroy, channel_stack,
+ &filters[0], filters.size(), &channel_args,
+ fixture.flags & REQUIRES_TRANSPORT
+ ? &dummy_transport::dummy_transport
+ : nullptr,
+ "CHANNEL", channel_stack)));
+ grpc_exec_ctx_flush(&exec_ctx);
+ grpc_call_stack *call_stack = static_cast<grpc_call_stack *>(
+ gpr_zalloc(channel_stack->call_stack_size));
+ gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+ gpr_timespec start_time = gpr_now(GPR_CLOCK_MONOTONIC);
+ grpc_slice method = grpc_slice_from_static_string("/foo/bar");
+ grpc_call_final_info final_info;
+ TestOp test_op_data;
+ while (state.KeepRunning()) {
+ GRPC_ERROR_UNREF(grpc_call_stack_init(&exec_ctx, channel_stack, 1,
+ DoNothing, NULL, NULL, NULL, method,
+ start_time, deadline, call_stack));
+ typename TestOp::Op op(&exec_ctx, &test_op_data, call_stack);
+ grpc_call_stack_destroy(&exec_ctx, call_stack, &final_info, NULL);
+ op.Finish(&exec_ctx);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_channel_stack_destroy(&exec_ctx, channel_stack);
+ grpc_exec_ctx_finish(&exec_ctx);
+ gpr_free(channel_stack);
+ gpr_free(call_stack);
+
+ state.SetLabel(label.str());
+}
+
+typedef Fixture<nullptr, 0> NoFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, NoFilter, NoOp);
+typedef Fixture<&dummy_filter::dummy_filter, 0> DummyFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, DummyFilter, NoOp);
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, DummyFilter, SendEmptyMetadata);
+typedef Fixture<&grpc_client_channel_filter, 0> ClientChannelFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, ClientChannelFilter, NoOp);
+typedef Fixture<&grpc_compress_filter, CHECKS_NOT_LAST> CompressFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, CompressFilter, NoOp);
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, CompressFilter, SendEmptyMetadata);
+typedef Fixture<&grpc_client_deadline_filter, CHECKS_NOT_LAST>
+ ClientDeadlineFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, ClientDeadlineFilter, NoOp);
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, ClientDeadlineFilter, SendEmptyMetadata);
+typedef Fixture<&grpc_server_deadline_filter, CHECKS_NOT_LAST>
+ ServerDeadlineFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, ServerDeadlineFilter, NoOp);
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, ServerDeadlineFilter, SendEmptyMetadata);
+typedef Fixture<&grpc_http_client_filter, CHECKS_NOT_LAST | REQUIRES_TRANSPORT>
+ HttpClientFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, HttpClientFilter, NoOp);
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, HttpClientFilter, SendEmptyMetadata);
+typedef Fixture<&grpc_http_server_filter, CHECKS_NOT_LAST> HttpServerFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, HttpServerFilter, NoOp);
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, HttpServerFilter, SendEmptyMetadata);
+typedef Fixture<&grpc_message_size_filter, CHECKS_NOT_LAST> MessageSizeFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, MessageSizeFilter, NoOp);
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, MessageSizeFilter, SendEmptyMetadata);
+typedef Fixture<&grpc_load_reporting_filter, CHECKS_NOT_LAST>
+ LoadReportingFilter;
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, LoadReportingFilter, NoOp);
+BENCHMARK_TEMPLATE(BM_IsolatedFilter, LoadReportingFilter, SendEmptyMetadata);
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/microbenchmarks/bm_chttp2_hpack.cc b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc
new file mode 100644
index 0000000000..5fb3f37130
--- /dev/null
+++ b/test/cpp/microbenchmarks/bm_chttp2_hpack.cc
@@ -0,0 +1,443 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* Microbenchmarks around CHTTP2 HPACK operations */
+
+#include <grpc/support/log.h>
+#include <string.h>
+#include <sstream>
+extern "C" {
+#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h"
+#include "src/core/ext/transport/chttp2/transport/hpack_parser.h"
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/transport/static_metadata.h"
+}
+#include "third_party/benchmark/include/benchmark/benchmark.h"
+
+static struct Init {
+ Init() { grpc_init(); }
+ ~Init() { grpc_shutdown(); }
+} g_init;
+
+////////////////////////////////////////////////////////////////////////////////
+// HPACK encoder
+//
+
+static void BM_HpackEncoderInitDestroy(benchmark::State &state) {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_chttp2_hpack_compressor c;
+ while (state.KeepRunning()) {
+ grpc_chttp2_hpack_compressor_init(&c);
+ grpc_chttp2_hpack_compressor_destroy(&exec_ctx, &c);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_HpackEncoderInitDestroy);
+
+template <class Fixture>
+static void BM_HpackEncoderEncodeHeader(benchmark::State &state) {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+ grpc_metadata_batch b;
+ grpc_metadata_batch_init(&b);
+ std::vector<grpc_mdelem> elems = Fixture::GetElems(&exec_ctx);
+ std::vector<grpc_linked_mdelem> storage(elems.size());
+ for (size_t i = 0; i < elems.size(); i++) {
+ GPR_ASSERT(GRPC_LOG_IF_ERROR(
+ "addmd",
+ grpc_metadata_batch_add_tail(&exec_ctx, &b, &storage[i], elems[i])));
+ }
+
+ grpc_chttp2_hpack_compressor c;
+ grpc_chttp2_hpack_compressor_init(&c);
+ grpc_transport_one_way_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ grpc_slice_buffer outbuf;
+ grpc_slice_buffer_init(&outbuf);
+ while (state.KeepRunning()) {
+ grpc_chttp2_encode_header(&exec_ctx, &c, (uint32_t)state.iterations(), &b,
+ state.range(0), state.range(1), &stats, &outbuf);
+ grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, &outbuf);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_metadata_batch_destroy(&exec_ctx, &b);
+ grpc_chttp2_hpack_compressor_destroy(&exec_ctx, &c);
+ grpc_slice_buffer_destroy_internal(&exec_ctx, &outbuf);
+ grpc_exec_ctx_finish(&exec_ctx);
+
+ std::ostringstream label;
+ label << "framing_bytes/iter:" << (static_cast<double>(stats.framing_bytes) /
+ static_cast<double>(state.iterations()))
+ << " header_bytes/iter:" << (static_cast<double>(stats.header_bytes) /
+ static_cast<double>(state.iterations()));
+ state.SetLabel(label.str());
+}
+
+namespace hpack_encoder_fixtures {
+
+class EmptyBatch {
+ public:
+ static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+ return {};
+ }
+};
+
+class SingleStaticElem {
+ public:
+ static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+ return {GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE};
+ }
+};
+
+class SingleInternedElem {
+ public:
+ static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+ return {grpc_mdelem_from_slices(
+ exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc")),
+ grpc_slice_intern(grpc_slice_from_static_string("def")))};
+ }
+};
+
+class SingleInternedKeyElem {
+ public:
+ static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+ return {grpc_mdelem_from_slices(
+ exec_ctx, grpc_slice_intern(grpc_slice_from_static_string("abc")),
+ grpc_slice_from_static_string("def"))};
+ }
+};
+
+class SingleNonInternedElem {
+ public:
+ static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+ return {grpc_mdelem_from_slices(exec_ctx,
+ grpc_slice_from_static_string("abc"),
+ grpc_slice_from_static_string("def"))};
+ }
+};
+
+class RepresentativeClientInitialMetadata {
+ public:
+ static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+ return {
+ GRPC_MDELEM_SCHEME_HTTP, GRPC_MDELEM_METHOD_POST,
+ grpc_mdelem_from_slices(
+ exec_ctx, GRPC_MDSTR_PATH,
+ grpc_slice_intern(grpc_slice_from_static_string("/foo/bar"))),
+ grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_AUTHORITY,
+ grpc_slice_intern(grpc_slice_from_static_string(
+ "foo.test.google.fr:1234"))),
+ GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP,
+ GRPC_MDELEM_TE_TRAILERS,
+ GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
+ grpc_mdelem_from_slices(
+ exec_ctx, GRPC_MDSTR_USER_AGENT,
+ grpc_slice_intern(grpc_slice_from_static_string(
+ "grpc-c/3.0.0-dev (linux; chttp2; green)")))};
+ }
+};
+
+class RepresentativeServerInitialMetadata {
+ public:
+ static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+ return {GRPC_MDELEM_STATUS_200,
+ GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC,
+ GRPC_MDELEM_GRPC_ACCEPT_ENCODING_IDENTITY_COMMA_DEFLATE_COMMA_GZIP};
+ }
+};
+
+class RepresentativeServerTrailingMetadata {
+ public:
+ static std::vector<grpc_mdelem> GetElems(grpc_exec_ctx *exec_ctx) {
+ return {GRPC_MDELEM_GRPC_STATUS_0};
+ }
+};
+
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({0, 16384});
+// test with eof (shouldn't affect anything)
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, EmptyBatch)->Args({1, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleStaticElem)
+ ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedKeyElem)
+ ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleInternedElem)
+ ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem)
+ ->Args({0, 16384});
+// test with a tiny frame size, to highlight continuation costs
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader, SingleNonInternedElem)
+ ->Args({0, 1});
+
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
+ RepresentativeClientInitialMetadata)
+ ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
+ RepresentativeServerInitialMetadata)
+ ->Args({0, 16384});
+BENCHMARK_TEMPLATE(BM_HpackEncoderEncodeHeader,
+ RepresentativeServerTrailingMetadata)
+ ->Args({1, 16384});
+
+} // namespace hpack_encoder_fixtures
+
+////////////////////////////////////////////////////////////////////////////////
+// HPACK parser
+//
+
+static void BM_HpackParserInitDestroy(benchmark::State &state) {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_chttp2_hpack_parser p;
+ while (state.KeepRunning()) {
+ grpc_chttp2_hpack_parser_init(&exec_ctx, &p);
+ grpc_chttp2_hpack_parser_destroy(&exec_ctx, &p);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_HpackParserInitDestroy);
+
+static void UnrefHeader(grpc_exec_ctx *exec_ctx, void *user_data,
+ grpc_mdelem md) {
+ GRPC_MDELEM_UNREF(exec_ctx, md);
+}
+
+template <class Fixture>
+static void BM_HpackParserParseHeader(benchmark::State &state) {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ std::vector<grpc_slice> init_slices = Fixture::GetInitSlices();
+ std::vector<grpc_slice> benchmark_slices = Fixture::GetBenchmarkSlices();
+ grpc_chttp2_hpack_parser p;
+ grpc_chttp2_hpack_parser_init(&exec_ctx, &p);
+ p.on_header = UnrefHeader;
+ p.on_header_user_data = nullptr;
+ for (auto slice : init_slices) {
+ grpc_chttp2_hpack_parser_parse(&exec_ctx, &p, slice);
+ }
+ while (state.KeepRunning()) {
+ for (auto slice : benchmark_slices) {
+ grpc_chttp2_hpack_parser_parse(&exec_ctx, &p, slice);
+ }
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_chttp2_hpack_parser_destroy(&exec_ctx, &p);
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+
+namespace hpack_parser_fixtures {
+
+static grpc_slice MakeSlice(std::initializer_list<uint8_t> bytes) {
+ grpc_slice s = grpc_slice_malloc(bytes.size());
+ uint8_t *p = GRPC_SLICE_START_PTR(s);
+ for (auto b : bytes) {
+ *p++ = b;
+ }
+ return s;
+}
+
+class EmptyBatch {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() { return {}; }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ return {MakeSlice({})};
+ }
+};
+
+class IndexedSingleStaticElem {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() {
+ return {MakeSlice(
+ {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})};
+ }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ return {MakeSlice({0xbe})};
+ }
+};
+
+class AddIndexedSingleStaticElem {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() { return {}; }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ return {MakeSlice(
+ {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})};
+ }
+};
+
+class KeyIndexedSingleStaticElem {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() {
+ return {MakeSlice(
+ {0x40, 0x07, ':', 's', 't', 'a', 't', 'u', 's', 0x03, '2', '0', '0'})};
+ }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ return {MakeSlice({0x7e, 0x03, 'd', 'e', 'f'})};
+ }
+};
+
+class IndexedSingleInternedElem {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() {
+ return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
+ }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ return {MakeSlice({0xbe})};
+ }
+};
+
+class AddIndexedSingleInternedElem {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() { return {}; }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
+ }
+};
+
+class KeyIndexedSingleInternedElem {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() {
+ return {MakeSlice({0x40, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
+ }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ return {MakeSlice({0x7e, 0x03, 'g', 'h', 'i'})};
+ }
+};
+
+class NonIndexedElem {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() { return {}; }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ return {MakeSlice({0x00, 0x03, 'a', 'b', 'c', 0x03, 'd', 'e', 'f'})};
+ }
+};
+
+class RepresentativeClientInitialMetadata {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() {
+ return {grpc_slice_from_static_string(
+ // generated with:
+ // ```
+ // tools/codegen/core/gen_header_frame.py --compression inc --no_framing
+ // < test/core/bad_client/tests/simple_request.headers
+ // ```
+ "@\x05:path\x08/foo/bar"
+ "@\x07:scheme\x04http"
+ "@\x07:method\x04POST"
+ "@\x0a:authority\x09localhost"
+ "@\x0c"
+ "content-type\x10"
+ "application/grpc"
+ "@\x14grpc-accept-encoding\x15identity,deflate,gzip"
+ "@\x02te\x08trailers"
+ "@\x0auser-agent\"bad-client grpc-c/0.12.0.0 (linux)")};
+ }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ // generated with:
+ // ```
+ // tools/codegen/core/gen_header_frame.py --compression pre --no_framing
+ // --hex < test/core/bad_client/tests/simple_request.headers
+ // ```
+ return {MakeSlice({0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe})};
+ }
+};
+
+class RepresentativeServerInitialMetadata {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() {
+ return {grpc_slice_from_static_string(
+ // generated with:
+ // ```
+ // tools/codegen/core/gen_header_frame.py --compression inc --no_framing
+ // <
+ // test/cpp/microbenchmarks/representative_server_initial_metadata.headers
+ // ```
+ "@\x07:status\x03"
+ "200"
+ "@\x0c"
+ "content-type\x10"
+ "application/grpc"
+ "@\x14grpc-accept-encoding\x15identity,deflate,gzip")};
+ }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ // generated with:
+ // ```
+ // tools/codegen/core/gen_header_frame.py --compression pre --no_framing
+ // --hex <
+ // test/cpp/microbenchmarks/representative_server_initial_metadata.headers
+ // ```
+ return {MakeSlice({0xc0, 0xbf, 0xbe})};
+ }
+};
+
+class RepresentativeServerTrailingMetadata {
+ public:
+ static std::vector<grpc_slice> GetInitSlices() {
+ return {grpc_slice_from_static_string(
+ // generated with:
+ // ```
+ // tools/codegen/core/gen_header_frame.py --compression inc --no_framing
+ // <
+ // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
+ // ```
+ "@\x0bgrpc-status\x01"
+ "0"
+ "@\x0cgrpc-message\x00")};
+ }
+ static std::vector<grpc_slice> GetBenchmarkSlices() {
+ // generated with:
+ // ```
+ // tools/codegen/core/gen_header_frame.py --compression pre --no_framing
+ // --hex <
+ // test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
+ // ```
+ return {MakeSlice({0xbf, 0xbe})};
+ }
+};
+
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, EmptyBatch);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleStaticElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleStaticElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleStaticElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, IndexedSingleInternedElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, AddIndexedSingleInternedElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, KeyIndexedSingleInternedElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader, NonIndexedElem);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
+ RepresentativeClientInitialMetadata);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
+ RepresentativeServerInitialMetadata);
+BENCHMARK_TEMPLATE(BM_HpackParserParseHeader,
+ RepresentativeServerTrailingMetadata);
+
+} // namespace hpack_parser_fixtures
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/microbenchmarks/bm_closure.cc b/test/cpp/microbenchmarks/bm_closure.cc
new file mode 100644
index 0000000000..1f54e8c8b1
--- /dev/null
+++ b/test/cpp/microbenchmarks/bm_closure.cc
@@ -0,0 +1,464 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* Test various closure related operations */
+
+#include <grpc/grpc.h>
+
+extern "C" {
+#include "src/core/lib/iomgr/closure.h"
+#include "src/core/lib/iomgr/combiner.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/support/spinlock.h"
+}
+
+#include "third_party/benchmark/include/benchmark/benchmark.h"
+
+#include <sstream>
+
+#ifdef GPR_LOW_LEVEL_COUNTERS
+extern "C" gpr_atm gpr_mu_locks;
+#endif
+
+static class InitializeStuff {
+ public:
+ InitializeStuff() { grpc_init(); }
+ ~InitializeStuff() { grpc_shutdown(); }
+} initialize_stuff;
+
+class TrackCounters {
+ public:
+ TrackCounters(benchmark::State& state) : state_(state) {}
+
+ ~TrackCounters() {
+ std::ostringstream out;
+#ifdef GPR_LOW_LEVEL_COUNTERS
+ out << " locks/iter:" << ((double)(gpr_atm_no_barrier_load(&gpr_mu_locks) -
+ mu_locks_at_start_) /
+ (double)state_.iterations())
+ << " atm_cas/iter:"
+ << ((double)(gpr_atm_no_barrier_load(&gpr_counter_atm_cas) -
+ atm_cas_at_start_) /
+ (double)state_.iterations())
+ << " atm_add/iter:"
+ << ((double)(gpr_atm_no_barrier_load(&gpr_counter_atm_add) -
+ atm_add_at_start_) /
+ (double)state_.iterations());
+#endif
+ state_.SetLabel(out.str());
+ }
+
+ private:
+ benchmark::State& state_;
+#ifdef GPR_LOW_LEVEL_COUNTERS
+ const size_t mu_locks_at_start_ = gpr_atm_no_barrier_load(&gpr_mu_locks);
+ const size_t atm_cas_at_start_ =
+ gpr_atm_no_barrier_load(&gpr_counter_atm_cas);
+ const size_t atm_add_at_start_ =
+ gpr_atm_no_barrier_load(&gpr_counter_atm_add);
+#endif
+};
+
+static void BM_NoOpExecCtx(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ while (state.KeepRunning()) {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_exec_ctx_finish(&exec_ctx);
+ }
+}
+BENCHMARK(BM_NoOpExecCtx);
+
+static void BM_WellFlushed(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_WellFlushed);
+
+static void DoNothing(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {}
+
+static void BM_ClosureInitAgainstExecCtx(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_closure c;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(
+ grpc_closure_init(&c, DoNothing, NULL, grpc_schedule_on_exec_ctx));
+ }
+}
+BENCHMARK(BM_ClosureInitAgainstExecCtx);
+
+static void BM_ClosureInitAgainstCombiner(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_combiner* combiner = grpc_combiner_create(NULL);
+ grpc_closure c;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(grpc_closure_init(
+ &c, DoNothing, NULL, grpc_combiner_scheduler(combiner, false)));
+ }
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished");
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureInitAgainstCombiner);
+
+static void BM_ClosureRunOnExecCtx(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_closure c;
+ grpc_closure_init(&c, DoNothing, NULL, grpc_schedule_on_exec_ctx);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_run(&exec_ctx, &c, GRPC_ERROR_NONE);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureRunOnExecCtx);
+
+static void BM_ClosureCreateAndRun(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_run(&exec_ctx, grpc_closure_create(DoNothing, NULL,
+ grpc_schedule_on_exec_ctx),
+ GRPC_ERROR_NONE);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureCreateAndRun);
+
+static void BM_ClosureInitAndRun(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_closure c;
+ while (state.KeepRunning()) {
+ grpc_closure_run(&exec_ctx, grpc_closure_init(&c, DoNothing, NULL,
+ grpc_schedule_on_exec_ctx),
+ GRPC_ERROR_NONE);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureInitAndRun);
+
+static void BM_ClosureSchedOnExecCtx(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_closure c;
+ grpc_closure_init(&c, DoNothing, NULL, grpc_schedule_on_exec_ctx);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_sched(&exec_ctx, &c, GRPC_ERROR_NONE);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureSchedOnExecCtx);
+
+static void BM_ClosureSched2OnExecCtx(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_closure c1;
+ grpc_closure c2;
+ grpc_closure_init(&c1, DoNothing, NULL, grpc_schedule_on_exec_ctx);
+ grpc_closure_init(&c2, DoNothing, NULL, grpc_schedule_on_exec_ctx);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureSched2OnExecCtx);
+
+static void BM_ClosureSched3OnExecCtx(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_closure c1;
+ grpc_closure c2;
+ grpc_closure c3;
+ grpc_closure_init(&c1, DoNothing, NULL, grpc_schedule_on_exec_ctx);
+ grpc_closure_init(&c2, DoNothing, NULL, grpc_schedule_on_exec_ctx);
+ grpc_closure_init(&c3, DoNothing, NULL, grpc_schedule_on_exec_ctx);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c3, GRPC_ERROR_NONE);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureSched3OnExecCtx);
+
+static void BM_AcquireMutex(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ // for comparison with the combiner stuff below
+ gpr_mu mu;
+ gpr_mu_init(&mu);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ gpr_mu_lock(&mu);
+ DoNothing(&exec_ctx, NULL, GRPC_ERROR_NONE);
+ gpr_mu_unlock(&mu);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_AcquireMutex);
+
+static void BM_TryAcquireMutex(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ // for comparison with the combiner stuff below
+ gpr_mu mu;
+ gpr_mu_init(&mu);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ if (gpr_mu_trylock(&mu)) {
+ DoNothing(&exec_ctx, NULL, GRPC_ERROR_NONE);
+ gpr_mu_unlock(&mu);
+ } else {
+ abort();
+ }
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_TryAcquireMutex);
+
+static void BM_AcquireSpinlock(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ // for comparison with the combiner stuff below
+ gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ gpr_spinlock_lock(&mu);
+ DoNothing(&exec_ctx, NULL, GRPC_ERROR_NONE);
+ gpr_spinlock_unlock(&mu);
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_AcquireSpinlock);
+
+static void BM_TryAcquireSpinlock(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ // for comparison with the combiner stuff below
+ gpr_spinlock mu = GPR_SPINLOCK_INITIALIZER;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ if (gpr_spinlock_trylock(&mu)) {
+ DoNothing(&exec_ctx, NULL, GRPC_ERROR_NONE);
+ gpr_spinlock_unlock(&mu);
+ } else {
+ abort();
+ }
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_TryAcquireSpinlock);
+
+static void BM_ClosureSchedOnCombiner(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_combiner* combiner = grpc_combiner_create(NULL);
+ grpc_closure c;
+ grpc_closure_init(&c, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner, false));
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_sched(&exec_ctx, &c, GRPC_ERROR_NONE);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished");
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureSchedOnCombiner);
+
+static void BM_ClosureSched2OnCombiner(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_combiner* combiner = grpc_combiner_create(NULL);
+ grpc_closure c1;
+ grpc_closure c2;
+ grpc_closure_init(&c1, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner, false));
+ grpc_closure_init(&c2, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner, false));
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished");
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureSched2OnCombiner);
+
+static void BM_ClosureSched3OnCombiner(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_combiner* combiner = grpc_combiner_create(NULL);
+ grpc_closure c1;
+ grpc_closure c2;
+ grpc_closure c3;
+ grpc_closure_init(&c1, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner, false));
+ grpc_closure_init(&c2, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner, false));
+ grpc_closure_init(&c3, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner, false));
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c3, GRPC_ERROR_NONE);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished");
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureSched3OnCombiner);
+
+static void BM_ClosureSched2OnTwoCombiners(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_combiner* combiner1 = grpc_combiner_create(NULL);
+ grpc_combiner* combiner2 = grpc_combiner_create(NULL);
+ grpc_closure c1;
+ grpc_closure c2;
+ grpc_closure_init(&c1, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner1, false));
+ grpc_closure_init(&c2, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner2, false));
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner1, "finished");
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner2, "finished");
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureSched2OnTwoCombiners);
+
+static void BM_ClosureSched4OnTwoCombiners(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_combiner* combiner1 = grpc_combiner_create(NULL);
+ grpc_combiner* combiner2 = grpc_combiner_create(NULL);
+ grpc_closure c1;
+ grpc_closure c2;
+ grpc_closure c3;
+ grpc_closure c4;
+ grpc_closure_init(&c1, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner1, false));
+ grpc_closure_init(&c2, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner2, false));
+ grpc_closure_init(&c3, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner1, false));
+ grpc_closure_init(&c4, DoNothing, NULL,
+ grpc_combiner_scheduler(combiner2, false));
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_closure_sched(&exec_ctx, &c1, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c2, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c3, GRPC_ERROR_NONE);
+ grpc_closure_sched(&exec_ctx, &c4, GRPC_ERROR_NONE);
+ grpc_exec_ctx_flush(&exec_ctx);
+ }
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner1, "finished");
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner2, "finished");
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureSched4OnTwoCombiners);
+
+// Helper that continuously reschedules the same closure against something until
+// the benchmark is complete
+class Rescheduler {
+ public:
+ Rescheduler(benchmark::State& state, grpc_closure_scheduler* scheduler)
+ : state_(state) {
+ grpc_closure_init(&closure_, Step, this, scheduler);
+ }
+
+ void ScheduleFirst(grpc_exec_ctx* exec_ctx) {
+ grpc_closure_sched(exec_ctx, &closure_, GRPC_ERROR_NONE);
+ }
+
+ void ScheduleFirstAgainstDifferentScheduler(
+ grpc_exec_ctx* exec_ctx, grpc_closure_scheduler* scheduler) {
+ grpc_closure_sched(exec_ctx, grpc_closure_create(Step, this, scheduler),
+ GRPC_ERROR_NONE);
+ }
+
+ private:
+ benchmark::State& state_;
+ grpc_closure closure_;
+
+ static void Step(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) {
+ Rescheduler* self = static_cast<Rescheduler*>(arg);
+ if (self->state_.KeepRunning()) {
+ grpc_closure_sched(exec_ctx, &self->closure_, GRPC_ERROR_NONE);
+ }
+ }
+};
+
+static void BM_ClosureReschedOnExecCtx(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ Rescheduler r(state, grpc_schedule_on_exec_ctx);
+ r.ScheduleFirst(&exec_ctx);
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureReschedOnExecCtx);
+
+static void BM_ClosureReschedOnCombiner(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_combiner* combiner = grpc_combiner_create(NULL);
+ Rescheduler r(state, grpc_combiner_scheduler(combiner, false));
+ r.ScheduleFirst(&exec_ctx);
+ grpc_exec_ctx_flush(&exec_ctx);
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished");
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureReschedOnCombiner);
+
+static void BM_ClosureReschedOnCombinerFinally(benchmark::State& state) {
+ TrackCounters track_counters(state);
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_combiner* combiner = grpc_combiner_create(NULL);
+ Rescheduler r(state, grpc_combiner_finally_scheduler(combiner, false));
+ r.ScheduleFirstAgainstDifferentScheduler(
+ &exec_ctx, grpc_combiner_scheduler(combiner, false));
+ grpc_exec_ctx_flush(&exec_ctx);
+ GRPC_COMBINER_UNREF(&exec_ctx, combiner, "finished");
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_ClosureReschedOnCombinerFinally);
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/microbenchmarks/bm_cq.cc b/test/cpp/microbenchmarks/bm_cq.cc
new file mode 100644
index 0000000000..c017474bf4
--- /dev/null
+++ b/test/cpp/microbenchmarks/bm_cq.cc
@@ -0,0 +1,145 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* This benchmark exists to ensure that the benchmark integration is
+ * working */
+
+#include <grpc++/completion_queue.h>
+#include <grpc++/impl/grpc_library.h>
+#include <grpc/grpc.h>
+
+#include "third_party/benchmark/include/benchmark/benchmark.h"
+
+extern "C" {
+#include "src/core/lib/surface/completion_queue.h"
+}
+
+namespace grpc {
+namespace testing {
+
+static class InitializeStuff {
+ public:
+ InitializeStuff() { init_lib_.init(); }
+ ~InitializeStuff() { init_lib_.shutdown(); }
+
+ private:
+ internal::GrpcLibrary init_lib_;
+ internal::GrpcLibraryInitializer init_;
+} initialize_stuff;
+
+static void BM_CreateDestroyCpp(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ CompletionQueue cq;
+ }
+}
+BENCHMARK(BM_CreateDestroyCpp);
+
+static void BM_CreateDestroyCore(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ grpc_completion_queue_destroy(grpc_completion_queue_create(NULL));
+ }
+}
+BENCHMARK(BM_CreateDestroyCore);
+
+static void DoneWithCompletionOnStack(grpc_exec_ctx* exec_ctx, void* arg,
+ grpc_cq_completion* completion) {}
+
+class DummyTag final : public CompletionQueueTag {
+ public:
+ bool FinalizeResult(void** tag, bool* status) override { return true; }
+};
+
+static void BM_Pass1Cpp(benchmark::State& state) {
+ CompletionQueue cq;
+ grpc_completion_queue* c_cq = cq.cq();
+ while (state.KeepRunning()) {
+ grpc_cq_completion completion;
+ DummyTag dummy_tag;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_cq_begin_op(c_cq, &dummy_tag);
+ grpc_cq_end_op(&exec_ctx, c_cq, &dummy_tag, GRPC_ERROR_NONE,
+ DoneWithCompletionOnStack, NULL, &completion);
+ grpc_exec_ctx_finish(&exec_ctx);
+ void* tag;
+ bool ok;
+ cq.Next(&tag, &ok);
+ }
+}
+BENCHMARK(BM_Pass1Cpp);
+
+static void BM_Pass1Core(benchmark::State& state) {
+ grpc_completion_queue* cq = grpc_completion_queue_create(NULL);
+ gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+ while (state.KeepRunning()) {
+ grpc_cq_completion completion;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_cq_begin_op(cq, NULL);
+ grpc_cq_end_op(&exec_ctx, cq, NULL, GRPC_ERROR_NONE,
+ DoneWithCompletionOnStack, NULL, &completion);
+ grpc_exec_ctx_finish(&exec_ctx);
+ grpc_completion_queue_next(cq, deadline, NULL);
+ }
+ grpc_completion_queue_destroy(cq);
+}
+BENCHMARK(BM_Pass1Core);
+
+static void BM_Pluck1Core(benchmark::State& state) {
+ grpc_completion_queue* cq = grpc_completion_queue_create(NULL);
+ gpr_timespec deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+ while (state.KeepRunning()) {
+ grpc_cq_completion completion;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_cq_begin_op(cq, NULL);
+ grpc_cq_end_op(&exec_ctx, cq, NULL, GRPC_ERROR_NONE,
+ DoneWithCompletionOnStack, NULL, &completion);
+ grpc_exec_ctx_finish(&exec_ctx);
+ grpc_completion_queue_pluck(cq, NULL, deadline, NULL);
+ }
+ grpc_completion_queue_destroy(cq);
+}
+BENCHMARK(BM_Pluck1Core);
+
+static void BM_EmptyCore(benchmark::State& state) {
+ grpc_completion_queue* cq = grpc_completion_queue_create(NULL);
+ gpr_timespec deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC);
+ while (state.KeepRunning()) {
+ grpc_completion_queue_next(cq, deadline, NULL);
+ }
+ grpc_completion_queue_destroy(cq);
+}
+BENCHMARK(BM_EmptyCore);
+
+} // namespace testing
+} // namespace grpc
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/microbenchmarks/bm_error.cc b/test/cpp/microbenchmarks/bm_error.cc
new file mode 100644
index 0000000000..8a4b86f281
--- /dev/null
+++ b/test/cpp/microbenchmarks/bm_error.cc
@@ -0,0 +1,248 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* Test various operations on grpc_error */
+
+#include <memory>
+
+extern "C" {
+#include "src/core/lib/iomgr/error.h"
+#include "src/core/lib/transport/error_utils.h"
+}
+
+#include "third_party/benchmark/include/benchmark/benchmark.h"
+
+class ErrorDeleter {
+ public:
+ void operator()(grpc_error* error) { GRPC_ERROR_UNREF(error); }
+};
+typedef std::unique_ptr<grpc_error, ErrorDeleter> ErrorPtr;
+
+static void BM_ErrorCreate(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ GRPC_ERROR_UNREF(GRPC_ERROR_CREATE("Error"));
+ }
+}
+BENCHMARK(BM_ErrorCreate);
+
+static void BM_ErrorCreateAndSetStatus(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ GRPC_ERROR_UNREF(grpc_error_set_int(GRPC_ERROR_CREATE("Error"),
+ GRPC_ERROR_INT_GRPC_STATUS,
+ GRPC_STATUS_ABORTED));
+ }
+}
+BENCHMARK(BM_ErrorCreateAndSetStatus);
+
+static void BM_ErrorRefUnref(benchmark::State& state) {
+ grpc_error* error = GRPC_ERROR_CREATE("Error");
+ while (state.KeepRunning()) {
+ GRPC_ERROR_UNREF(GRPC_ERROR_REF(error));
+ }
+ GRPC_ERROR_UNREF(error);
+}
+BENCHMARK(BM_ErrorRefUnref);
+
+static void BM_ErrorUnrefNone(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ GRPC_ERROR_UNREF(GRPC_ERROR_NONE);
+ }
+}
+BENCHMARK(BM_ErrorUnrefNone);
+
+static void BM_ErrorGetIntFromNoError(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ intptr_t value;
+ grpc_error_get_int(GRPC_ERROR_NONE, GRPC_ERROR_INT_GRPC_STATUS, &value);
+ }
+}
+BENCHMARK(BM_ErrorGetIntFromNoError);
+
+static void BM_ErrorGetMissingInt(benchmark::State& state) {
+ ErrorPtr error(
+ grpc_error_set_int(GRPC_ERROR_CREATE("Error"), GRPC_ERROR_INT_INDEX, 1));
+ while (state.KeepRunning()) {
+ intptr_t value;
+ grpc_error_get_int(error.get(), GRPC_ERROR_INT_OFFSET, &value);
+ }
+}
+BENCHMARK(BM_ErrorGetMissingInt);
+
+static void BM_ErrorGetPresentInt(benchmark::State& state) {
+ ErrorPtr error(
+ grpc_error_set_int(GRPC_ERROR_CREATE("Error"), GRPC_ERROR_INT_OFFSET, 1));
+ while (state.KeepRunning()) {
+ intptr_t value;
+ grpc_error_get_int(error.get(), GRPC_ERROR_INT_OFFSET, &value);
+ }
+}
+BENCHMARK(BM_ErrorGetPresentInt);
+
+// Fixtures for tests: generate different kinds of errors
+class ErrorNone {
+ public:
+ gpr_timespec deadline() const { return deadline_; }
+ grpc_error* error() const { return GRPC_ERROR_NONE; }
+
+ private:
+ const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+};
+
+class ErrorCancelled {
+ public:
+ gpr_timespec deadline() const { return deadline_; }
+ grpc_error* error() const { return GRPC_ERROR_CANCELLED; }
+
+ private:
+ const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+};
+
+class SimpleError {
+ public:
+ gpr_timespec deadline() const { return deadline_; }
+ grpc_error* error() const { return error_.get(); }
+
+ private:
+ const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+ ErrorPtr error_{GRPC_ERROR_CREATE("Error")};
+};
+
+class ErrorWithGrpcStatus {
+ public:
+ gpr_timespec deadline() const { return deadline_; }
+ grpc_error* error() const { return error_.get(); }
+
+ private:
+ const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+ ErrorPtr error_{grpc_error_set_int(GRPC_ERROR_CREATE("Error"),
+ GRPC_ERROR_INT_GRPC_STATUS,
+ GRPC_STATUS_UNIMPLEMENTED)};
+};
+
+class ErrorWithHttpError {
+ public:
+ gpr_timespec deadline() const { return deadline_; }
+ grpc_error* error() const { return error_.get(); }
+
+ private:
+ const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+ ErrorPtr error_{grpc_error_set_int(GRPC_ERROR_CREATE("Error"),
+ GRPC_ERROR_INT_HTTP2_ERROR,
+ GRPC_HTTP2_COMPRESSION_ERROR)};
+};
+
+class ErrorWithNestedGrpcStatus {
+ public:
+ gpr_timespec deadline() const { return deadline_; }
+ grpc_error* error() const { return error_.get(); }
+
+ private:
+ const gpr_timespec deadline_ = gpr_inf_future(GPR_CLOCK_MONOTONIC);
+ ErrorPtr nested_error_{grpc_error_set_int(GRPC_ERROR_CREATE("Error"),
+ GRPC_ERROR_INT_GRPC_STATUS,
+ GRPC_STATUS_UNIMPLEMENTED)};
+ grpc_error* nested_errors_[1] = {nested_error_.get()};
+ ErrorPtr error_{GRPC_ERROR_CREATE_REFERENCING("Error", nested_errors_, 1)};
+};
+
+template <class Fixture>
+static void BM_ErrorStringOnNewError(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ Fixture fixture;
+ grpc_error_string(fixture.error());
+ }
+}
+
+template <class Fixture>
+static void BM_ErrorStringRepeatedly(benchmark::State& state) {
+ Fixture fixture;
+ while (state.KeepRunning()) {
+ grpc_error_string(fixture.error());
+ }
+}
+
+template <class Fixture>
+static void BM_ErrorGetStatus(benchmark::State& state) {
+ Fixture fixture;
+ while (state.KeepRunning()) {
+ grpc_status_code status;
+ const char* msg;
+ grpc_error_get_status(fixture.error(), fixture.deadline(), &status, &msg,
+ NULL);
+ }
+}
+
+template <class Fixture>
+static void BM_ErrorGetStatusCode(benchmark::State& state) {
+ Fixture fixture;
+ while (state.KeepRunning()) {
+ grpc_status_code status;
+ grpc_error_get_status(fixture.error(), fixture.deadline(), &status, NULL,
+ NULL);
+ }
+}
+
+template <class Fixture>
+static void BM_ErrorHttpError(benchmark::State& state) {
+ Fixture fixture;
+ while (state.KeepRunning()) {
+ grpc_http2_error_code error;
+ grpc_error_get_status(fixture.error(), fixture.deadline(), NULL, NULL,
+ &error);
+ }
+}
+
+template <class Fixture>
+static void BM_HasClearGrpcStatus(benchmark::State& state) {
+ Fixture fixture;
+ while (state.KeepRunning()) {
+ grpc_error_has_clear_grpc_status(fixture.error());
+ }
+}
+
+#define BENCHMARK_SUITE(fixture) \
+ BENCHMARK_TEMPLATE(BM_ErrorStringOnNewError, fixture); \
+ BENCHMARK_TEMPLATE(BM_ErrorStringRepeatedly, fixture); \
+ BENCHMARK_TEMPLATE(BM_ErrorGetStatus, fixture); \
+ BENCHMARK_TEMPLATE(BM_ErrorGetStatusCode, fixture); \
+ BENCHMARK_TEMPLATE(BM_ErrorHttpError, fixture); \
+ BENCHMARK_TEMPLATE(BM_HasClearGrpcStatus, fixture)
+
+BENCHMARK_SUITE(ErrorNone);
+BENCHMARK_SUITE(ErrorCancelled);
+BENCHMARK_SUITE(SimpleError);
+BENCHMARK_SUITE(ErrorWithGrpcStatus);
+BENCHMARK_SUITE(ErrorWithHttpError);
+BENCHMARK_SUITE(ErrorWithNestedGrpcStatus);
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/microbenchmarks/bm_fullstack.cc b/test/cpp/microbenchmarks/bm_fullstack.cc
new file mode 100644
index 0000000000..48e131f1be
--- /dev/null
+++ b/test/cpp/microbenchmarks/bm_fullstack.cc
@@ -0,0 +1,1079 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/* Benchmark gRPC end2end in various configurations */
+
+#include <sstream>
+
+#include <grpc++/channel.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/impl/grpc_library.h>
+#include <grpc++/security/credentials.h>
+#include <grpc++/security/server_credentials.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc/support/log.h>
+
+extern "C" {
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/ext/transport/chttp2/transport/internal.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/endpoint_pair.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/completion_queue.h"
+#include "src/core/lib/surface/server.h"
+#include "test/core/util/memory_counters.h"
+#include "test/core/util/passthru_endpoint.h"
+#include "test/core/util/port.h"
+#include "test/core/util/trickle_endpoint.h"
+}
+#include "src/core/lib/profiling/timers.h"
+#include "src/cpp/client/create_channel_internal.h"
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "third_party/benchmark/include/benchmark/benchmark.h"
+
+namespace grpc {
+namespace testing {
+
+static class InitializeStuff {
+ public:
+ InitializeStuff() {
+ grpc_memory_counters_init();
+ init_lib_.init();
+ rq_ = grpc_resource_quota_create("bm");
+ }
+
+ ~InitializeStuff() { init_lib_.shutdown(); }
+
+ grpc_resource_quota* rq() { return rq_; }
+
+ private:
+ internal::GrpcLibrary init_lib_;
+ grpc_resource_quota* rq_;
+} initialize_stuff;
+
+/*******************************************************************************
+ * FIXTURES
+ */
+
+static void ApplyCommonServerBuilderConfig(ServerBuilder* b) {
+ b->SetMaxReceiveMessageSize(INT_MAX);
+ b->SetMaxSendMessageSize(INT_MAX);
+}
+
+static void ApplyCommonChannelArguments(ChannelArguments* c) {
+ c->SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, INT_MAX);
+ c->SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, INT_MAX);
+}
+
+#ifdef GPR_LOW_LEVEL_COUNTERS
+extern "C" gpr_atm gpr_mu_locks;
+extern "C" gpr_atm gpr_counter_atm_cas;
+extern "C" gpr_atm gpr_counter_atm_add;
+#endif
+
+class BaseFixture {
+ public:
+ void Finish(benchmark::State& s) {
+ std::ostringstream out;
+ this->AddToLabel(out, s);
+#ifdef GPR_LOW_LEVEL_COUNTERS
+ out << " locks/iter:" << ((double)(gpr_atm_no_barrier_load(&gpr_mu_locks) -
+ mu_locks_at_start_) /
+ (double)s.iterations())
+ << " atm_cas/iter:"
+ << ((double)(gpr_atm_no_barrier_load(&gpr_counter_atm_cas) -
+ atm_cas_at_start_) /
+ (double)s.iterations())
+ << " atm_add/iter:"
+ << ((double)(gpr_atm_no_barrier_load(&gpr_counter_atm_add) -
+ atm_add_at_start_) /
+ (double)s.iterations());
+#endif
+ grpc_memory_counters counters_at_end = grpc_memory_counters_snapshot();
+ out << " allocs/iter:"
+ << ((double)(counters_at_end.total_allocs_absolute -
+ counters_at_start_.total_allocs_absolute) /
+ (double)s.iterations());
+ auto label = out.str();
+ if (label.length() && label[0] == ' ') {
+ label = label.substr(1);
+ }
+ s.SetLabel(label);
+ }
+
+ virtual void AddToLabel(std::ostream& out, benchmark::State& s) = 0;
+
+ private:
+#ifdef GPR_LOW_LEVEL_COUNTERS
+ const size_t mu_locks_at_start_ = gpr_atm_no_barrier_load(&gpr_mu_locks);
+ const size_t atm_cas_at_start_ =
+ gpr_atm_no_barrier_load(&gpr_counter_atm_cas);
+ const size_t atm_add_at_start_ =
+ gpr_atm_no_barrier_load(&gpr_counter_atm_add);
+#endif
+ grpc_memory_counters counters_at_start_ = grpc_memory_counters_snapshot();
+};
+
+class FullstackFixture : public BaseFixture {
+ public:
+ FullstackFixture(Service* service, const grpc::string& address) {
+ ServerBuilder b;
+ b.AddListeningPort(address, InsecureServerCredentials());
+ cq_ = b.AddCompletionQueue(true);
+ b.RegisterService(service);
+ ApplyCommonServerBuilderConfig(&b);
+ server_ = b.BuildAndStart();
+ ChannelArguments args;
+ ApplyCommonChannelArguments(&args);
+ channel_ = CreateCustomChannel(address, InsecureChannelCredentials(), args);
+ }
+
+ virtual ~FullstackFixture() {
+ server_->Shutdown();
+ cq_->Shutdown();
+ void* tag;
+ bool ok;
+ while (cq_->Next(&tag, &ok)) {
+ }
+ }
+
+ ServerCompletionQueue* cq() { return cq_.get(); }
+ std::shared_ptr<Channel> channel() { return channel_; }
+
+ private:
+ std::unique_ptr<Server> server_;
+ std::unique_ptr<ServerCompletionQueue> cq_;
+ std::shared_ptr<Channel> channel_;
+};
+
+class TCP : public FullstackFixture {
+ public:
+ TCP(Service* service) : FullstackFixture(service, MakeAddress()) {}
+
+ void AddToLabel(std::ostream& out, benchmark::State& state) {}
+
+ private:
+ static grpc::string MakeAddress() {
+ int port = grpc_pick_unused_port_or_die();
+ std::stringstream addr;
+ addr << "localhost:" << port;
+ return addr.str();
+ }
+};
+
+class UDS : public FullstackFixture {
+ public:
+ UDS(Service* service) : FullstackFixture(service, MakeAddress()) {}
+
+ void AddToLabel(std::ostream& out, benchmark::State& state) override {}
+
+ private:
+ static grpc::string MakeAddress() {
+ int port = grpc_pick_unused_port_or_die(); // just for a unique id - not a
+ // real port
+ std::stringstream addr;
+ addr << "unix:/tmp/bm_fullstack." << port;
+ return addr.str();
+ }
+};
+
+class EndpointPairFixture : public BaseFixture {
+ public:
+ EndpointPairFixture(Service* service, grpc_endpoint_pair endpoints)
+ : endpoint_pair_(endpoints) {
+ ServerBuilder b;
+ cq_ = b.AddCompletionQueue(true);
+ b.RegisterService(service);
+ ApplyCommonServerBuilderConfig(&b);
+ server_ = b.BuildAndStart();
+
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+ /* add server endpoint to server_ */
+ {
+ const grpc_channel_args* server_args =
+ grpc_server_get_channel_args(server_->c_server());
+ server_transport_ = grpc_create_chttp2_transport(
+ &exec_ctx, server_args, endpoints.server, 0 /* is_client */);
+
+ grpc_pollset** pollsets;
+ size_t num_pollsets = 0;
+ grpc_server_get_pollsets(server_->c_server(), &pollsets, &num_pollsets);
+
+ for (size_t i = 0; i < num_pollsets; i++) {
+ grpc_endpoint_add_to_pollset(&exec_ctx, endpoints.server, pollsets[i]);
+ }
+
+ grpc_server_setup_transport(&exec_ctx, server_->c_server(),
+ server_transport_, NULL, server_args);
+ grpc_chttp2_transport_start_reading(&exec_ctx, server_transport_, NULL);
+ }
+
+ /* create channel */
+ {
+ ChannelArguments args;
+ args.SetString(GRPC_ARG_DEFAULT_AUTHORITY, "test.authority");
+ ApplyCommonChannelArguments(&args);
+
+ grpc_channel_args c_args = args.c_channel_args();
+ client_transport_ =
+ grpc_create_chttp2_transport(&exec_ctx, &c_args, endpoints.client, 1);
+ GPR_ASSERT(client_transport_);
+ grpc_channel* channel =
+ grpc_channel_create(&exec_ctx, "target", &c_args,
+ GRPC_CLIENT_DIRECT_CHANNEL, client_transport_);
+ grpc_chttp2_transport_start_reading(&exec_ctx, client_transport_, NULL);
+
+ channel_ = CreateChannelInternal("", channel);
+ }
+
+ grpc_exec_ctx_finish(&exec_ctx);
+ }
+
+ virtual ~EndpointPairFixture() {
+ server_->Shutdown();
+ cq_->Shutdown();
+ void* tag;
+ bool ok;
+ while (cq_->Next(&tag, &ok)) {
+ }
+ }
+
+ ServerCompletionQueue* cq() { return cq_.get(); }
+ std::shared_ptr<Channel> channel() { return channel_; }
+
+ protected:
+ grpc_endpoint_pair endpoint_pair_;
+ grpc_transport* client_transport_;
+ grpc_transport* server_transport_;
+
+ private:
+ std::unique_ptr<Server> server_;
+ std::unique_ptr<ServerCompletionQueue> cq_;
+ std::shared_ptr<Channel> channel_;
+};
+
+class SockPair : public EndpointPairFixture {
+ public:
+ SockPair(Service* service)
+ : EndpointPairFixture(service, grpc_iomgr_create_endpoint_pair(
+ "test", initialize_stuff.rq(), 8192)) {
+ }
+
+ void AddToLabel(std::ostream& out, benchmark::State& state) {}
+};
+
+class InProcessCHTTP2 : public EndpointPairFixture {
+ public:
+ InProcessCHTTP2(Service* service)
+ : EndpointPairFixture(service, MakeEndpoints()) {}
+
+ void AddToLabel(std::ostream& out, benchmark::State& state) {
+ out << " writes/iter:"
+ << ((double)stats_.num_writes / (double)state.iterations());
+ }
+
+ private:
+ grpc_passthru_endpoint_stats stats_;
+
+ grpc_endpoint_pair MakeEndpoints() {
+ grpc_endpoint_pair p;
+ grpc_passthru_endpoint_create(&p.client, &p.server, initialize_stuff.rq(),
+ &stats_);
+ return p;
+ }
+};
+
+class TrickledCHTTP2 : public EndpointPairFixture {
+ public:
+ TrickledCHTTP2(Service* service, size_t megabits_per_second)
+ : EndpointPairFixture(service, MakeEndpoints(megabits_per_second)) {}
+
+ void AddToLabel(std::ostream& out, benchmark::State& state) {
+ out << " writes/iter:"
+ << ((double)stats_.num_writes / (double)state.iterations())
+ << " cli_transport_stalls/iter:"
+ << ((double)
+ client_stats_.streams_stalled_due_to_transport_flow_control /
+ (double)state.iterations())
+ << " cli_stream_stalls/iter:"
+ << ((double)client_stats_.streams_stalled_due_to_stream_flow_control /
+ (double)state.iterations())
+ << " svr_transport_stalls/iter:"
+ << ((double)
+ server_stats_.streams_stalled_due_to_transport_flow_control /
+ (double)state.iterations())
+ << " svr_stream_stalls/iter:"
+ << ((double)server_stats_.streams_stalled_due_to_stream_flow_control /
+ (double)state.iterations());
+ }
+
+ void Step() {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ size_t client_backlog =
+ grpc_trickle_endpoint_trickle(&exec_ctx, endpoint_pair_.client);
+ size_t server_backlog =
+ grpc_trickle_endpoint_trickle(&exec_ctx, endpoint_pair_.server);
+ grpc_exec_ctx_finish(&exec_ctx);
+
+ UpdateStats((grpc_chttp2_transport*)client_transport_, &client_stats_,
+ client_backlog);
+ UpdateStats((grpc_chttp2_transport*)server_transport_, &server_stats_,
+ server_backlog);
+ }
+
+ private:
+ grpc_passthru_endpoint_stats stats_;
+ struct Stats {
+ int streams_stalled_due_to_stream_flow_control = 0;
+ int streams_stalled_due_to_transport_flow_control = 0;
+ };
+ Stats client_stats_;
+ Stats server_stats_;
+
+ grpc_endpoint_pair MakeEndpoints(size_t kilobits) {
+ grpc_endpoint_pair p;
+ grpc_passthru_endpoint_create(&p.client, &p.server, initialize_stuff.rq(),
+ &stats_);
+ double bytes_per_second = 125.0 * kilobits;
+ p.client = grpc_trickle_endpoint_create(p.client, bytes_per_second);
+ p.server = grpc_trickle_endpoint_create(p.server, bytes_per_second);
+ return p;
+ }
+
+ void UpdateStats(grpc_chttp2_transport* t, Stats* s, size_t backlog) {
+ if (backlog == 0) {
+ if (t->lists[GRPC_CHTTP2_LIST_STALLED_BY_STREAM].head != NULL) {
+ s->streams_stalled_due_to_stream_flow_control++;
+ }
+ if (t->lists[GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT].head != NULL) {
+ s->streams_stalled_due_to_transport_flow_control++;
+ }
+ }
+ }
+};
+
+/*******************************************************************************
+ * CONTEXT MUTATORS
+ */
+
+static const int kPregenerateKeyCount = 100000;
+
+template <class F>
+auto MakeVector(size_t length, F f) -> std::vector<decltype(f())> {
+ std::vector<decltype(f())> out;
+ out.reserve(length);
+ for (size_t i = 0; i < length; i++) {
+ out.push_back(f());
+ }
+ return out;
+}
+
+class NoOpMutator {
+ public:
+ template <class ContextType>
+ NoOpMutator(ContextType* context) {}
+};
+
+template <int length>
+class RandomBinaryMetadata {
+ public:
+ static const grpc::string& Key() { return kKey; }
+
+ static const grpc::string& Value() {
+ return kValues[rand() % kValues.size()];
+ }
+
+ private:
+ static const grpc::string kKey;
+ static const std::vector<grpc::string> kValues;
+
+ static grpc::string GenerateOneString() {
+ grpc::string s;
+ s.reserve(length + 1);
+ for (int i = 0; i < length; i++) {
+ s += (char)rand();
+ }
+ return s;
+ }
+};
+
+template <int length>
+const grpc::string RandomBinaryMetadata<length>::kKey = "foo-bin";
+
+template <int length>
+const std::vector<grpc::string> RandomBinaryMetadata<length>::kValues =
+ MakeVector(kPregenerateKeyCount, GenerateOneString);
+
+template <int length>
+class RandomAsciiMetadata {
+ public:
+ static const grpc::string& Key() { return kKey; }
+
+ static const grpc::string& Value() {
+ return kValues[rand() % kValues.size()];
+ }
+
+ private:
+ static const grpc::string kKey;
+ static const std::vector<grpc::string> kValues;
+
+ static grpc::string GenerateOneString() {
+ grpc::string s;
+ s.reserve(length + 1);
+ for (int i = 0; i < length; i++) {
+ s += (char)(rand() % 26 + 'a');
+ }
+ return s;
+ }
+};
+
+template <int length>
+const grpc::string RandomAsciiMetadata<length>::kKey = "foo";
+
+template <int length>
+const std::vector<grpc::string> RandomAsciiMetadata<length>::kValues =
+ MakeVector(kPregenerateKeyCount, GenerateOneString);
+
+template <class Generator, int kNumKeys>
+class Client_AddMetadata : public NoOpMutator {
+ public:
+ Client_AddMetadata(ClientContext* context) : NoOpMutator(context) {
+ for (int i = 0; i < kNumKeys; i++) {
+ context->AddMetadata(Generator::Key(), Generator::Value());
+ }
+ }
+};
+
+template <class Generator, int kNumKeys>
+class Server_AddInitialMetadata : public NoOpMutator {
+ public:
+ Server_AddInitialMetadata(ServerContext* context) : NoOpMutator(context) {
+ for (int i = 0; i < kNumKeys; i++) {
+ context->AddInitialMetadata(Generator::Key(), Generator::Value());
+ }
+ }
+};
+
+/*******************************************************************************
+ * BENCHMARKING KERNELS
+ */
+
+static void* tag(intptr_t x) { return reinterpret_cast<void*>(x); }
+
+template <class Fixture, class ClientContextMutator, class ServerContextMutator>
+static void BM_UnaryPingPong(benchmark::State& state) {
+ EchoTestService::AsyncService service;
+ std::unique_ptr<Fixture> fixture(new Fixture(&service));
+ EchoRequest send_request;
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ if (state.range(0) > 0) {
+ send_request.set_message(std::string(state.range(0), 'a'));
+ }
+ if (state.range(1) > 0) {
+ send_response.set_message(std::string(state.range(1), 'a'));
+ }
+ Status recv_status;
+ struct ServerEnv {
+ ServerContext ctx;
+ EchoRequest recv_request;
+ grpc::ServerAsyncResponseWriter<EchoResponse> response_writer;
+ ServerEnv() : response_writer(&ctx) {}
+ };
+ uint8_t server_env_buffer[2 * sizeof(ServerEnv)];
+ ServerEnv* server_env[2] = {
+ reinterpret_cast<ServerEnv*>(server_env_buffer),
+ reinterpret_cast<ServerEnv*>(server_env_buffer + sizeof(ServerEnv))};
+ new (server_env[0]) ServerEnv;
+ new (server_env[1]) ServerEnv;
+ service.RequestEcho(&server_env[0]->ctx, &server_env[0]->recv_request,
+ &server_env[0]->response_writer, fixture->cq(),
+ fixture->cq(), tag(0));
+ service.RequestEcho(&server_env[1]->ctx, &server_env[1]->recv_request,
+ &server_env[1]->response_writer, fixture->cq(),
+ fixture->cq(), tag(1));
+ std::unique_ptr<EchoTestService::Stub> stub(
+ EchoTestService::NewStub(fixture->channel()));
+ while (state.KeepRunning()) {
+ GPR_TIMER_SCOPE("BenchmarkCycle", 0);
+ recv_response.Clear();
+ ClientContext cli_ctx;
+ ClientContextMutator cli_ctx_mut(&cli_ctx);
+ std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader(
+ stub->AsyncEcho(&cli_ctx, send_request, fixture->cq()));
+ void* t;
+ bool ok;
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ GPR_ASSERT(t == tag(0) || t == tag(1));
+ intptr_t slot = reinterpret_cast<intptr_t>(t);
+ ServerEnv* senv = server_env[slot];
+ ServerContextMutator svr_ctx_mut(&senv->ctx);
+ senv->response_writer.Finish(send_response, Status::OK, tag(3));
+ response_reader->Finish(&recv_response, &recv_status, tag(4));
+ for (int i = (1 << 3) | (1 << 4); i != 0;) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ int tagnum = (int)reinterpret_cast<intptr_t>(t);
+ GPR_ASSERT(i & (1 << tagnum));
+ i -= 1 << tagnum;
+ }
+ GPR_ASSERT(recv_status.ok());
+
+ senv->~ServerEnv();
+ senv = new (senv) ServerEnv();
+ service.RequestEcho(&senv->ctx, &senv->recv_request, &senv->response_writer,
+ fixture->cq(), fixture->cq(), tag(slot));
+ }
+ fixture->Finish(state);
+ fixture.reset();
+ server_env[0]->~ServerEnv();
+ server_env[1]->~ServerEnv();
+ state.SetBytesProcessed(state.range(0) * state.iterations() +
+ state.range(1) * state.iterations());
+}
+
+// Repeatedly makes Streaming Bidi calls (exchanging a configurable number of
+// messages in each call) in a loop on a single channel
+//
+// First parmeter (i.e state.range(0)): Message size (in bytes) to use
+// Second parameter (i.e state.range(1)): Number of ping pong messages.
+// Note: One ping-pong means two messages (one from client to server and
+// the other from server to client):
+template <class Fixture, class ClientContextMutator, class ServerContextMutator>
+static void BM_StreamingPingPong(benchmark::State& state) {
+ const int msg_size = state.range(0);
+ const int max_ping_pongs = state.range(1);
+
+ EchoTestService::AsyncService service;
+ std::unique_ptr<Fixture> fixture(new Fixture(&service));
+ {
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ EchoRequest send_request;
+ EchoRequest recv_request;
+
+ if (msg_size > 0) {
+ send_request.set_message(std::string(msg_size, 'a'));
+ send_response.set_message(std::string(msg_size, 'b'));
+ }
+
+ std::unique_ptr<EchoTestService::Stub> stub(
+ EchoTestService::NewStub(fixture->channel()));
+
+ while (state.KeepRunning()) {
+ ServerContext svr_ctx;
+ ServerContextMutator svr_ctx_mut(&svr_ctx);
+ ServerAsyncReaderWriter<EchoResponse, EchoRequest> response_rw(&svr_ctx);
+ service.RequestBidiStream(&svr_ctx, &response_rw, fixture->cq(),
+ fixture->cq(), tag(0));
+
+ ClientContext cli_ctx;
+ ClientContextMutator cli_ctx_mut(&cli_ctx);
+ auto request_rw = stub->AsyncBidiStream(&cli_ctx, fixture->cq(), tag(1));
+
+ // Establish async stream between client side and server side
+ void* t;
+ bool ok;
+ int need_tags = (1 << 0) | (1 << 1);
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+
+ // Send 'max_ping_pongs' number of ping pong messages
+ int ping_pong_cnt = 0;
+ while (ping_pong_cnt < max_ping_pongs) {
+ request_rw->Write(send_request, tag(0)); // Start client send
+ response_rw.Read(&recv_request, tag(1)); // Start server recv
+ request_rw->Read(&recv_response, tag(2)); // Start client recv
+
+ need_tags = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3);
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ int i = (int)(intptr_t)t;
+
+ // If server recv is complete, start the server send operation
+ if (i == 1) {
+ response_rw.Write(send_response, tag(3));
+ }
+
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+
+ ping_pong_cnt++;
+ }
+
+ request_rw->WritesDone(tag(0));
+ response_rw.Finish(Status::OK, tag(1));
+
+ Status recv_status;
+ request_rw->Finish(&recv_status, tag(2));
+
+ need_tags = (1 << 0) | (1 << 1) | (1 << 2);
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+
+ GPR_ASSERT(recv_status.ok());
+ }
+ }
+
+ fixture->Finish(state);
+ fixture.reset();
+ state.SetBytesProcessed(msg_size * state.iterations() * max_ping_pongs * 2);
+}
+
+// Repeatedly sends ping pong messages in a single streaming Bidi call in a loop
+// First parmeter (i.e state.range(0)): Message size (in bytes) to use
+template <class Fixture, class ClientContextMutator, class ServerContextMutator>
+static void BM_StreamingPingPongMsgs(benchmark::State& state) {
+ const int msg_size = state.range(0);
+
+ EchoTestService::AsyncService service;
+ std::unique_ptr<Fixture> fixture(new Fixture(&service));
+ {
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ EchoRequest send_request;
+ EchoRequest recv_request;
+
+ if (msg_size > 0) {
+ send_request.set_message(std::string(msg_size, 'a'));
+ send_response.set_message(std::string(msg_size, 'b'));
+ }
+
+ std::unique_ptr<EchoTestService::Stub> stub(
+ EchoTestService::NewStub(fixture->channel()));
+
+ ServerContext svr_ctx;
+ ServerContextMutator svr_ctx_mut(&svr_ctx);
+ ServerAsyncReaderWriter<EchoResponse, EchoRequest> response_rw(&svr_ctx);
+ service.RequestBidiStream(&svr_ctx, &response_rw, fixture->cq(),
+ fixture->cq(), tag(0));
+
+ ClientContext cli_ctx;
+ ClientContextMutator cli_ctx_mut(&cli_ctx);
+ auto request_rw = stub->AsyncBidiStream(&cli_ctx, fixture->cq(), tag(1));
+
+ // Establish async stream between client side and server side
+ void* t;
+ bool ok;
+ int need_tags = (1 << 0) | (1 << 1);
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+
+ while (state.KeepRunning()) {
+ GPR_TIMER_SCOPE("BenchmarkCycle", 0);
+ request_rw->Write(send_request, tag(0)); // Start client send
+ response_rw.Read(&recv_request, tag(1)); // Start server recv
+ request_rw->Read(&recv_response, tag(2)); // Start client recv
+
+ need_tags = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3);
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ int i = (int)(intptr_t)t;
+
+ // If server recv is complete, start the server send operation
+ if (i == 1) {
+ response_rw.Write(send_response, tag(3));
+ }
+
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+ }
+
+ request_rw->WritesDone(tag(0));
+ response_rw.Finish(Status::OK, tag(1));
+ Status recv_status;
+ request_rw->Finish(&recv_status, tag(2));
+
+ need_tags = (1 << 0) | (1 << 1) | (1 << 2);
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+
+ GPR_ASSERT(recv_status.ok());
+ }
+
+ fixture->Finish(state);
+ fixture.reset();
+ state.SetBytesProcessed(msg_size * state.iterations() * 2);
+}
+
+template <class Fixture>
+static void BM_PumpStreamClientToServer(benchmark::State& state) {
+ EchoTestService::AsyncService service;
+ std::unique_ptr<Fixture> fixture(new Fixture(&service));
+ {
+ EchoRequest send_request;
+ EchoRequest recv_request;
+ if (state.range(0) > 0) {
+ send_request.set_message(std::string(state.range(0), 'a'));
+ }
+ Status recv_status;
+ ServerContext svr_ctx;
+ ServerAsyncReaderWriter<EchoResponse, EchoRequest> response_rw(&svr_ctx);
+ service.RequestBidiStream(&svr_ctx, &response_rw, fixture->cq(),
+ fixture->cq(), tag(0));
+ std::unique_ptr<EchoTestService::Stub> stub(
+ EchoTestService::NewStub(fixture->channel()));
+ ClientContext cli_ctx;
+ auto request_rw = stub->AsyncBidiStream(&cli_ctx, fixture->cq(), tag(1));
+ int need_tags = (1 << 0) | (1 << 1);
+ void* t;
+ bool ok;
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+ response_rw.Read(&recv_request, tag(0));
+ while (state.KeepRunning()) {
+ GPR_TIMER_SCOPE("BenchmarkCycle", 0);
+ request_rw->Write(send_request, tag(1));
+ while (true) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ if (t == tag(0)) {
+ response_rw.Read(&recv_request, tag(0));
+ } else if (t == tag(1)) {
+ break;
+ } else {
+ GPR_ASSERT(false);
+ }
+ }
+ }
+ request_rw->WritesDone(tag(1));
+ need_tags = (1 << 0) | (1 << 1);
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+ }
+ fixture->Finish(state);
+ fixture.reset();
+ state.SetBytesProcessed(state.range(0) * state.iterations());
+}
+
+template <class Fixture>
+static void BM_PumpStreamServerToClient(benchmark::State& state) {
+ EchoTestService::AsyncService service;
+ std::unique_ptr<Fixture> fixture(new Fixture(&service));
+ {
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ if (state.range(0) > 0) {
+ send_response.set_message(std::string(state.range(0), 'a'));
+ }
+ Status recv_status;
+ ServerContext svr_ctx;
+ ServerAsyncReaderWriter<EchoResponse, EchoRequest> response_rw(&svr_ctx);
+ service.RequestBidiStream(&svr_ctx, &response_rw, fixture->cq(),
+ fixture->cq(), tag(0));
+ std::unique_ptr<EchoTestService::Stub> stub(
+ EchoTestService::NewStub(fixture->channel()));
+ ClientContext cli_ctx;
+ auto request_rw = stub->AsyncBidiStream(&cli_ctx, fixture->cq(), tag(1));
+ int need_tags = (1 << 0) | (1 << 1);
+ void* t;
+ bool ok;
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+ request_rw->Read(&recv_response, tag(0));
+ while (state.KeepRunning()) {
+ GPR_TIMER_SCOPE("BenchmarkCycle", 0);
+ response_rw.Write(send_response, tag(1));
+ while (true) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ if (t == tag(0)) {
+ request_rw->Read(&recv_response, tag(0));
+ } else if (t == tag(1)) {
+ break;
+ } else {
+ GPR_ASSERT(false);
+ }
+ }
+ }
+ response_rw.Finish(Status::OK, tag(1));
+ need_tags = (1 << 0) | (1 << 1);
+ while (need_tags) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+ }
+ fixture->Finish(state);
+ fixture.reset();
+ state.SetBytesProcessed(state.range(0) * state.iterations());
+}
+
+static void TrickleCQNext(TrickledCHTTP2* fixture, void** t, bool* ok) {
+ while (true) {
+ switch (fixture->cq()->AsyncNext(
+ t, ok, gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC),
+ gpr_time_from_micros(100, GPR_TIMESPAN)))) {
+ case CompletionQueue::TIMEOUT:
+ fixture->Step();
+ break;
+ case CompletionQueue::SHUTDOWN:
+ GPR_ASSERT(false);
+ break;
+ case CompletionQueue::GOT_EVENT:
+ return;
+ }
+ }
+}
+
+static void BM_PumpStreamServerToClient_Trickle(benchmark::State& state) {
+ EchoTestService::AsyncService service;
+ std::unique_ptr<TrickledCHTTP2> fixture(
+ new TrickledCHTTP2(&service, state.range(1)));
+ {
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ if (state.range(0) > 0) {
+ send_response.set_message(std::string(state.range(0), 'a'));
+ }
+ Status recv_status;
+ ServerContext svr_ctx;
+ ServerAsyncReaderWriter<EchoResponse, EchoRequest> response_rw(&svr_ctx);
+ service.RequestBidiStream(&svr_ctx, &response_rw, fixture->cq(),
+ fixture->cq(), tag(0));
+ std::unique_ptr<EchoTestService::Stub> stub(
+ EchoTestService::NewStub(fixture->channel()));
+ ClientContext cli_ctx;
+ auto request_rw = stub->AsyncBidiStream(&cli_ctx, fixture->cq(), tag(1));
+ int need_tags = (1 << 0) | (1 << 1);
+ void* t;
+ bool ok;
+ while (need_tags) {
+ TrickleCQNext(fixture.get(), &t, &ok);
+ GPR_ASSERT(ok);
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+ request_rw->Read(&recv_response, tag(0));
+ while (state.KeepRunning()) {
+ GPR_TIMER_SCOPE("BenchmarkCycle", 0);
+ response_rw.Write(send_response, tag(1));
+ while (true) {
+ TrickleCQNext(fixture.get(), &t, &ok);
+ if (t == tag(0)) {
+ request_rw->Read(&recv_response, tag(0));
+ } else if (t == tag(1)) {
+ break;
+ } else {
+ GPR_ASSERT(false);
+ }
+ }
+ }
+ response_rw.Finish(Status::OK, tag(1));
+ need_tags = (1 << 0) | (1 << 1);
+ while (need_tags) {
+ TrickleCQNext(fixture.get(), &t, &ok);
+ int i = (int)(intptr_t)t;
+ GPR_ASSERT(need_tags & (1 << i));
+ need_tags &= ~(1 << i);
+ }
+ }
+ fixture->Finish(state);
+ fixture.reset();
+ state.SetBytesProcessed(state.range(0) * state.iterations());
+}
+
+/*******************************************************************************
+ * CONFIGURATIONS
+ */
+
+static void SweepSizesArgs(benchmark::internal::Benchmark* b) {
+ b->Args({0, 0});
+ for (int i = 1; i <= 128 * 1024 * 1024; i *= 8) {
+ b->Args({i, 0});
+ b->Args({0, i});
+ b->Args({i, i});
+ }
+}
+
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, TCP, NoOpMutator, NoOpMutator)
+ ->Apply(SweepSizesArgs);
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, UDS, NoOpMutator, NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, SockPair, NoOpMutator, NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator, NoOpMutator)
+ ->Apply(SweepSizesArgs);
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2,
+ Client_AddMetadata<RandomBinaryMetadata<10>, 1>, NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2,
+ Client_AddMetadata<RandomBinaryMetadata<31>, 1>, NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2,
+ Client_AddMetadata<RandomBinaryMetadata<100>, 1>,
+ NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2,
+ Client_AddMetadata<RandomBinaryMetadata<10>, 2>, NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2,
+ Client_AddMetadata<RandomBinaryMetadata<31>, 2>, NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2,
+ Client_AddMetadata<RandomBinaryMetadata<100>, 2>,
+ NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator,
+ Server_AddInitialMetadata<RandomBinaryMetadata<10>, 1>)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator,
+ Server_AddInitialMetadata<RandomBinaryMetadata<31>, 1>)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator,
+ Server_AddInitialMetadata<RandomBinaryMetadata<100>, 1>)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2,
+ Client_AddMetadata<RandomAsciiMetadata<10>, 1>, NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2,
+ Client_AddMetadata<RandomAsciiMetadata<31>, 1>, NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2,
+ Client_AddMetadata<RandomAsciiMetadata<100>, 1>, NoOpMutator)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator,
+ Server_AddInitialMetadata<RandomAsciiMetadata<10>, 1>)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator,
+ Server_AddInitialMetadata<RandomAsciiMetadata<31>, 1>)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator,
+ Server_AddInitialMetadata<RandomAsciiMetadata<100>, 1>)
+ ->Args({0, 0});
+BENCHMARK_TEMPLATE(BM_UnaryPingPong, InProcessCHTTP2, NoOpMutator,
+ Server_AddInitialMetadata<RandomAsciiMetadata<10>, 100>)
+ ->Args({0, 0});
+
+BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, TCP)
+ ->Range(0, 128 * 1024 * 1024);
+BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, UDS)
+ ->Range(0, 128 * 1024 * 1024);
+BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, SockPair)
+ ->Range(0, 128 * 1024 * 1024);
+BENCHMARK_TEMPLATE(BM_PumpStreamClientToServer, InProcessCHTTP2)
+ ->Range(0, 128 * 1024 * 1024);
+BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, TCP)
+ ->Range(0, 128 * 1024 * 1024);
+BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, UDS)
+ ->Range(0, 128 * 1024 * 1024);
+BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, SockPair)
+ ->Range(0, 128 * 1024 * 1024);
+BENCHMARK_TEMPLATE(BM_PumpStreamServerToClient, InProcessCHTTP2)
+ ->Range(0, 128 * 1024 * 1024);
+
+static void TrickleArgs(benchmark::internal::Benchmark* b) {
+ for (int i = 1; i <= 128 * 1024 * 1024; i *= 8) {
+ for (int j = 1; j <= 128 * 1024 * 1024; j *= 8) {
+ double expected_time =
+ static_cast<double>(14 + i) / (125.0 * static_cast<double>(j));
+ if (expected_time > 0.01) continue;
+ b->Args({i, j});
+ }
+ }
+}
+
+BENCHMARK(BM_PumpStreamServerToClient_Trickle)->Apply(TrickleArgs);
+
+// Generate Args for StreamingPingPong benchmarks. Currently generates args for
+// only "small streams" (i.e streams with 0, 1 or 2 messages)
+static void StreamingPingPongArgs(benchmark::internal::Benchmark* b) {
+ int msg_size = 0;
+
+ b->Args({0, 0}); // spl case: 0 ping-pong msgs (msg_size doesn't matter here)
+
+ for (msg_size = 0; msg_size <= 128 * 1024 * 1024;
+ msg_size == 0 ? msg_size++ : msg_size *= 8) {
+ b->Args({msg_size, 1});
+ b->Args({msg_size, 2});
+ }
+}
+
+BENCHMARK_TEMPLATE(BM_StreamingPingPong, InProcessCHTTP2, NoOpMutator,
+ NoOpMutator)
+ ->Apply(StreamingPingPongArgs);
+BENCHMARK_TEMPLATE(BM_StreamingPingPong, TCP, NoOpMutator, NoOpMutator)
+ ->Apply(StreamingPingPongArgs);
+
+BENCHMARK_TEMPLATE(BM_StreamingPingPongMsgs, InProcessCHTTP2, NoOpMutator,
+ NoOpMutator)
+ ->Range(0, 128 * 1024 * 1024);
+BENCHMARK_TEMPLATE(BM_StreamingPingPongMsgs, TCP, NoOpMutator, NoOpMutator)
+ ->Range(0, 128 * 1024 * 1024);
+
+} // namespace testing
+} // namespace grpc
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/microbenchmarks/bm_metadata.cc b/test/cpp/microbenchmarks/bm_metadata.cc
new file mode 100644
index 0000000000..f468690834
--- /dev/null
+++ b/test/cpp/microbenchmarks/bm_metadata.cc
@@ -0,0 +1,297 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/* Test out various metadata handling primitives */
+
+#include <grpc/grpc.h>
+
+extern "C" {
+#include "src/core/lib/slice/slice_internal.h"
+#include "src/core/lib/transport/metadata.h"
+#include "src/core/lib/transport/static_metadata.h"
+#include "src/core/lib/transport/transport.h"
+}
+
+#include "third_party/benchmark/include/benchmark/benchmark.h"
+
+static class InitializeStuff {
+ public:
+ InitializeStuff() { grpc_init(); }
+ ~InitializeStuff() { grpc_shutdown(); }
+} initialize_stuff;
+
+static void BM_SliceFromStatic(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ benchmark::DoNotOptimize(grpc_slice_from_static_string("abc"));
+ }
+}
+BENCHMARK(BM_SliceFromStatic);
+
+static void BM_SliceFromCopied(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ grpc_slice_unref(grpc_slice_from_copied_string("abc"));
+ }
+}
+BENCHMARK(BM_SliceFromCopied);
+
+static void BM_SliceFromStreamOwnedBuffer(benchmark::State& state) {
+ grpc_stream_refcount r;
+ GRPC_STREAM_REF_INIT(&r, 1, NULL, NULL, "test");
+ char buffer[64];
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ grpc_slice_unref_internal(&exec_ctx, grpc_slice_from_stream_owned_buffer(
+ &r, buffer, sizeof(buffer)));
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_SliceFromStreamOwnedBuffer);
+
+static void BM_SliceIntern(benchmark::State& state) {
+ gpr_slice slice = grpc_slice_from_static_string("abc");
+ while (state.KeepRunning()) {
+ grpc_slice_unref(grpc_slice_intern(slice));
+ }
+}
+BENCHMARK(BM_SliceIntern);
+
+static void BM_SliceReIntern(benchmark::State& state) {
+ gpr_slice slice = grpc_slice_intern(grpc_slice_from_static_string("abc"));
+ while (state.KeepRunning()) {
+ grpc_slice_unref(grpc_slice_intern(slice));
+ }
+ grpc_slice_unref(slice);
+}
+BENCHMARK(BM_SliceReIntern);
+
+static void BM_SliceInternStaticMetadata(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ grpc_slice_intern(GRPC_MDSTR_GZIP);
+ }
+}
+BENCHMARK(BM_SliceInternStaticMetadata);
+
+static void BM_SliceInternEqualToStaticMetadata(benchmark::State& state) {
+ gpr_slice slice = grpc_slice_from_static_string("gzip");
+ while (state.KeepRunning()) {
+ grpc_slice_intern(slice);
+ }
+}
+BENCHMARK(BM_SliceInternEqualToStaticMetadata);
+
+static void BM_MetadataFromNonInternedSlices(benchmark::State& state) {
+ gpr_slice k = grpc_slice_from_static_string("key");
+ gpr_slice v = grpc_slice_from_static_string("value");
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataFromNonInternedSlices);
+
+static void BM_MetadataFromInternedSlices(benchmark::State& state) {
+ gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+ gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value"));
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+ grpc_slice_unref(k);
+ grpc_slice_unref(v);
+}
+BENCHMARK(BM_MetadataFromInternedSlices);
+
+static void BM_MetadataFromInternedSlicesAlreadyInIndex(
+ benchmark::State& state) {
+ gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+ gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value"));
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_mdelem seed = grpc_mdelem_create(&exec_ctx, k, v, NULL);
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+ }
+ GRPC_MDELEM_UNREF(&exec_ctx, seed);
+ grpc_exec_ctx_finish(&exec_ctx);
+ grpc_slice_unref(k);
+ grpc_slice_unref(v);
+}
+BENCHMARK(BM_MetadataFromInternedSlicesAlreadyInIndex);
+
+static void BM_MetadataFromInternedKey(benchmark::State& state) {
+ gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+ gpr_slice v = grpc_slice_from_static_string("value");
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+ grpc_slice_unref(k);
+}
+BENCHMARK(BM_MetadataFromInternedKey);
+
+static void BM_MetadataFromNonInternedSlicesWithBackingStore(
+ benchmark::State& state) {
+ gpr_slice k = grpc_slice_from_static_string("key");
+ gpr_slice v = grpc_slice_from_static_string("value");
+ char backing_store[sizeof(grpc_mdelem_data)];
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(
+ &exec_ctx,
+ grpc_mdelem_create(&exec_ctx, k, v,
+ reinterpret_cast<grpc_mdelem_data*>(backing_store)));
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataFromNonInternedSlicesWithBackingStore);
+
+static void BM_MetadataFromInternedSlicesWithBackingStore(
+ benchmark::State& state) {
+ gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+ gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value"));
+ char backing_store[sizeof(grpc_mdelem_data)];
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(
+ &exec_ctx,
+ grpc_mdelem_create(&exec_ctx, k, v,
+ reinterpret_cast<grpc_mdelem_data*>(backing_store)));
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+ grpc_slice_unref(k);
+ grpc_slice_unref(v);
+}
+BENCHMARK(BM_MetadataFromInternedSlicesWithBackingStore);
+
+static void BM_MetadataFromInternedKeyWithBackingStore(
+ benchmark::State& state) {
+ gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+ gpr_slice v = grpc_slice_from_static_string("value");
+ char backing_store[sizeof(grpc_mdelem_data)];
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(
+ &exec_ctx,
+ grpc_mdelem_create(&exec_ctx, k, v,
+ reinterpret_cast<grpc_mdelem_data*>(backing_store)));
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+ grpc_slice_unref(k);
+}
+BENCHMARK(BM_MetadataFromInternedKeyWithBackingStore);
+
+static void BM_MetadataFromStaticMetadataStrings(benchmark::State& state) {
+ gpr_slice k = GRPC_MDSTR_STATUS;
+ gpr_slice v = GRPC_MDSTR_200;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+ grpc_slice_unref(k);
+}
+BENCHMARK(BM_MetadataFromStaticMetadataStrings);
+
+static void BM_MetadataFromStaticMetadataStringsNotIndexed(
+ benchmark::State& state) {
+ gpr_slice k = GRPC_MDSTR_STATUS;
+ gpr_slice v = GRPC_MDSTR_GZIP;
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, grpc_mdelem_create(&exec_ctx, k, v, NULL));
+ }
+ grpc_exec_ctx_finish(&exec_ctx);
+ grpc_slice_unref(k);
+}
+BENCHMARK(BM_MetadataFromStaticMetadataStringsNotIndexed);
+
+static void BM_MetadataRefUnrefExternal(benchmark::State& state) {
+ char backing_store[sizeof(grpc_mdelem_data)];
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_mdelem el =
+ grpc_mdelem_create(&exec_ctx, grpc_slice_from_static_string("a"),
+ grpc_slice_from_static_string("b"),
+ reinterpret_cast<grpc_mdelem_data*>(backing_store));
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el));
+ }
+ GRPC_MDELEM_UNREF(&exec_ctx, el);
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataRefUnrefExternal);
+
+static void BM_MetadataRefUnrefInterned(benchmark::State& state) {
+ char backing_store[sizeof(grpc_mdelem_data)];
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ gpr_slice k = grpc_slice_intern(grpc_slice_from_static_string("key"));
+ gpr_slice v = grpc_slice_intern(grpc_slice_from_static_string("value"));
+ grpc_mdelem el = grpc_mdelem_create(
+ &exec_ctx, k, v, reinterpret_cast<grpc_mdelem_data*>(backing_store));
+ grpc_slice_unref(k);
+ grpc_slice_unref(v);
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el));
+ }
+ GRPC_MDELEM_UNREF(&exec_ctx, el);
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataRefUnrefInterned);
+
+static void BM_MetadataRefUnrefAllocated(benchmark::State& state) {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_mdelem el =
+ grpc_mdelem_create(&exec_ctx, grpc_slice_from_static_string("a"),
+ grpc_slice_from_static_string("b"), NULL);
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el));
+ }
+ GRPC_MDELEM_UNREF(&exec_ctx, el);
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataRefUnrefAllocated);
+
+static void BM_MetadataRefUnrefStatic(benchmark::State& state) {
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+ grpc_mdelem el =
+ grpc_mdelem_create(&exec_ctx, GRPC_MDSTR_STATUS, GRPC_MDSTR_200, NULL);
+ while (state.KeepRunning()) {
+ GRPC_MDELEM_UNREF(&exec_ctx, GRPC_MDELEM_REF(el));
+ }
+ GRPC_MDELEM_UNREF(&exec_ctx, el);
+ grpc_exec_ctx_finish(&exec_ctx);
+}
+BENCHMARK(BM_MetadataRefUnrefStatic);
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/qps/limit_cores.h b/test/cpp/microbenchmarks/noop-benchmark.cc
index 5482904a3c..99fa6d5f6e 100644
--- a/test/cpp/qps/limit_cores.h
+++ b/test/cpp/microbenchmarks/noop-benchmark.cc
@@ -1,6 +1,6 @@
/*
*
- * Copyright 2016, Google Inc.
+ * Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -31,19 +31,15 @@
*
*/
-#ifndef TEST_QPS_LIMIT_CORES_H
-#define TEST_QPS_LIMIT_CORES_H
+/* This benchmark exists to ensure that the benchmark integration is
+ * working */
-namespace grpc {
-namespace testing {
-/// LimitCores: allow this worker to only run on the cores specified in the
-/// array \a cores, which is of length \a cores_size.
-///
-/// LimitCores takes array and size arguments (instead of vector) for direct
-/// conversion from repeated field of protobuf. Use a cores_size of 0 to remove
-/// existing limits (from an empty repeated field)
-int LimitCores(const int *cores, int cores_size);
-} // namespace testing
-} // namespace grpc
+#include "third_party/benchmark/include/benchmark/benchmark.h"
-#endif // TEST_QPS_LIMIT_CORES_H
+static void BM_NoOp(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ }
+}
+BENCHMARK(BM_NoOp);
+
+BENCHMARK_MAIN();
diff --git a/test/cpp/microbenchmarks/representative_server_initial_metadata.headers b/test/cpp/microbenchmarks/representative_server_initial_metadata.headers
new file mode 100644
index 0000000000..d3e6933366
--- /dev/null
+++ b/test/cpp/microbenchmarks/representative_server_initial_metadata.headers
@@ -0,0 +1,4 @@
+:status: 200
+content-type: application/grpc
+grpc-accept-encoding: identity,deflate,gzip
+
diff --git a/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers b/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
new file mode 100644
index 0000000000..544d089853
--- /dev/null
+++ b/test/cpp/microbenchmarks/representative_server_trailing_metadata.headers
@@ -0,0 +1,3 @@
+grpc-status: 0
+grpc-message:
+
diff --git a/test/cpp/performance/writes_per_rpc_test.cc b/test/cpp/performance/writes_per_rpc_test.cc
new file mode 100644
index 0000000000..7a914c1547
--- /dev/null
+++ b/test/cpp/performance/writes_per_rpc_test.cc
@@ -0,0 +1,268 @@
+/*
+ *
+ * Copyright 2017, 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 <grpc++/channel.h>
+#include <grpc++/create_channel.h>
+#include <grpc++/impl/grpc_library.h>
+#include <grpc++/security/credentials.h>
+#include <grpc++/security/server_credentials.h>
+#include <grpc++/server.h>
+#include <grpc++/server_builder.h>
+#include <grpc/support/log.h>
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h"
+#include "src/core/lib/channel/channel_args.h"
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/endpoint_pair.h"
+#include "src/core/lib/iomgr/exec_ctx.h"
+#include "src/core/lib/iomgr/tcp_posix.h"
+#include "src/core/lib/surface/channel.h"
+#include "src/core/lib/surface/completion_queue.h"
+#include "src/core/lib/surface/server.h"
+#include "test/core/util/passthru_endpoint.h"
+#include "test/core/util/port.h"
+}
+#include "src/cpp/client/create_channel_internal.h"
+#include "src/proto/grpc/testing/echo.grpc.pb.h"
+#include "test/core/util/test_config.h"
+
+namespace grpc {
+namespace testing {
+
+static void* tag(intptr_t x) { return reinterpret_cast<void*>(x); }
+
+static void ApplyCommonServerBuilderConfig(ServerBuilder* b) {
+ b->SetMaxReceiveMessageSize(INT_MAX);
+ b->SetMaxSendMessageSize(INT_MAX);
+}
+
+static void ApplyCommonChannelArguments(ChannelArguments* c) {
+ c->SetInt(GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH, INT_MAX);
+ c->SetInt(GRPC_ARG_MAX_SEND_MESSAGE_LENGTH, INT_MAX);
+}
+
+static class InitializeStuff {
+ public:
+ InitializeStuff() {
+ init_lib_.init();
+ rq_ = grpc_resource_quota_create("bm");
+ }
+
+ ~InitializeStuff() { init_lib_.shutdown(); }
+
+ grpc_resource_quota* rq() { return rq_; }
+
+ private:
+ internal::GrpcLibrary init_lib_;
+ grpc_resource_quota* rq_;
+} initialize_stuff;
+
+class EndpointPairFixture {
+ public:
+ EndpointPairFixture(Service* service, grpc_endpoint_pair endpoints) {
+ ServerBuilder b;
+ cq_ = b.AddCompletionQueue(true);
+ b.RegisterService(service);
+ ApplyCommonServerBuilderConfig(&b);
+ server_ = b.BuildAndStart();
+
+ grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
+ /* add server endpoint to server_ */
+ {
+ const grpc_channel_args* server_args =
+ grpc_server_get_channel_args(server_->c_server());
+ grpc_transport* transport = grpc_create_chttp2_transport(
+ &exec_ctx, server_args, endpoints.server, 0 /* is_client */);
+
+ grpc_pollset** pollsets;
+ size_t num_pollsets = 0;
+ grpc_server_get_pollsets(server_->c_server(), &pollsets, &num_pollsets);
+
+ for (size_t i = 0; i < num_pollsets; i++) {
+ grpc_endpoint_add_to_pollset(&exec_ctx, endpoints.server, pollsets[i]);
+ }
+
+ grpc_server_setup_transport(&exec_ctx, server_->c_server(), transport,
+ NULL, server_args);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
+ }
+
+ /* create channel */
+ {
+ ChannelArguments args;
+ args.SetString(GRPC_ARG_DEFAULT_AUTHORITY, "test.authority");
+ ApplyCommonChannelArguments(&args);
+
+ grpc_channel_args c_args = args.c_channel_args();
+ grpc_transport* transport =
+ grpc_create_chttp2_transport(&exec_ctx, &c_args, endpoints.client, 1);
+ GPR_ASSERT(transport);
+ grpc_channel* channel = grpc_channel_create(
+ &exec_ctx, "target", &c_args, GRPC_CLIENT_DIRECT_CHANNEL, transport);
+ grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL);
+
+ channel_ = CreateChannelInternal("", channel);
+ }
+
+ grpc_exec_ctx_finish(&exec_ctx);
+ }
+
+ virtual ~EndpointPairFixture() {
+ server_->Shutdown();
+ cq_->Shutdown();
+ void* tag;
+ bool ok;
+ while (cq_->Next(&tag, &ok)) {
+ }
+ }
+
+ ServerCompletionQueue* cq() { return cq_.get(); }
+ std::shared_ptr<Channel> channel() { return channel_; }
+
+ private:
+ std::unique_ptr<Server> server_;
+ std::unique_ptr<ServerCompletionQueue> cq_;
+ std::shared_ptr<Channel> channel_;
+};
+
+class InProcessCHTTP2 : public EndpointPairFixture {
+ public:
+ InProcessCHTTP2(Service* service)
+ : EndpointPairFixture(service, MakeEndpoints()) {}
+
+ int writes_performed() const { return stats_.num_writes; }
+
+ private:
+ grpc_passthru_endpoint_stats stats_;
+
+ grpc_endpoint_pair MakeEndpoints() {
+ grpc_endpoint_pair p;
+ grpc_passthru_endpoint_create(&p.client, &p.server, initialize_stuff.rq(),
+ &stats_);
+ return p;
+ }
+};
+
+static double UnaryPingPong(int request_size, int response_size) {
+ const int kIterations = 10000;
+
+ EchoTestService::AsyncService service;
+ std::unique_ptr<InProcessCHTTP2> fixture(new InProcessCHTTP2(&service));
+ EchoRequest send_request;
+ EchoResponse send_response;
+ EchoResponse recv_response;
+ if (request_size > 0) {
+ send_request.set_message(std::string(request_size, 'a'));
+ }
+ if (response_size > 0) {
+ send_response.set_message(std::string(response_size, 'a'));
+ }
+ Status recv_status;
+ struct ServerEnv {
+ ServerContext ctx;
+ EchoRequest recv_request;
+ grpc::ServerAsyncResponseWriter<EchoResponse> response_writer;
+ ServerEnv() : response_writer(&ctx) {}
+ };
+ uint8_t server_env_buffer[2 * sizeof(ServerEnv)];
+ ServerEnv* server_env[2] = {
+ reinterpret_cast<ServerEnv*>(server_env_buffer),
+ reinterpret_cast<ServerEnv*>(server_env_buffer + sizeof(ServerEnv))};
+ new (server_env[0]) ServerEnv;
+ new (server_env[1]) ServerEnv;
+ service.RequestEcho(&server_env[0]->ctx, &server_env[0]->recv_request,
+ &server_env[0]->response_writer, fixture->cq(),
+ fixture->cq(), tag(0));
+ service.RequestEcho(&server_env[1]->ctx, &server_env[1]->recv_request,
+ &server_env[1]->response_writer, fixture->cq(),
+ fixture->cq(), tag(1));
+ std::unique_ptr<EchoTestService::Stub> stub(
+ EchoTestService::NewStub(fixture->channel()));
+ for (int iteration = 0; iteration < kIterations; iteration++) {
+ recv_response.Clear();
+ ClientContext cli_ctx;
+ std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> response_reader(
+ stub->AsyncEcho(&cli_ctx, send_request, fixture->cq()));
+ void* t;
+ bool ok;
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ GPR_ASSERT(t == tag(0) || t == tag(1));
+ intptr_t slot = reinterpret_cast<intptr_t>(t);
+ ServerEnv* senv = server_env[slot];
+ senv->response_writer.Finish(send_response, Status::OK, tag(3));
+ response_reader->Finish(&recv_response, &recv_status, tag(4));
+ for (int i = (1 << 3) | (1 << 4); i != 0;) {
+ GPR_ASSERT(fixture->cq()->Next(&t, &ok));
+ GPR_ASSERT(ok);
+ int tagnum = (int)reinterpret_cast<intptr_t>(t);
+ GPR_ASSERT(i & (1 << tagnum));
+ i -= 1 << tagnum;
+ }
+ GPR_ASSERT(recv_status.ok());
+
+ senv->~ServerEnv();
+ senv = new (senv) ServerEnv();
+ service.RequestEcho(&senv->ctx, &senv->recv_request, &senv->response_writer,
+ fixture->cq(), fixture->cq(), tag(slot));
+ }
+
+ double writes_per_iteration =
+ (double)fixture->writes_performed() / (double)kIterations;
+
+ fixture.reset();
+ server_env[0]->~ServerEnv();
+ server_env[1]->~ServerEnv();
+
+ return writes_per_iteration;
+}
+
+TEST(WritesPerRpcTest, UnaryPingPong) {
+ EXPECT_LT(UnaryPingPong(0, 0), 2.05);
+ EXPECT_LT(UnaryPingPong(1, 0), 2.05);
+ EXPECT_LT(UnaryPingPong(0, 1), 2.05);
+ EXPECT_LT(UnaryPingPong(4096, 0), 2.2);
+ EXPECT_LT(UnaryPingPong(0, 4096), 2.2);
+}
+
+} // namespace testing
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ grpc_test_init(argc, argv);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h
index 5fb87b2782..baa9304cc2 100644
--- a/test/cpp/qps/client.h
+++ b/test/cpp/qps/client.h
@@ -36,6 +36,7 @@
#include <condition_variable>
#include <mutex>
+#include <unordered_map>
#include <vector>
#include <grpc++/channel.h>
@@ -50,7 +51,6 @@
#include "test/cpp/qps/histogram.h"
#include "test/cpp/qps/interarrival.h"
-#include "test/cpp/qps/limit_cores.h"
#include "test/cpp/qps/usage_timer.h"
#include "test/cpp/util/create_test_channel.h"
@@ -102,7 +102,7 @@ class ClientRequestCreator<ByteBuffer> {
if (payload_config.has_bytebuf_params()) {
std::unique_ptr<char[]> buf(
new char[payload_config.bytebuf_params().req_size()]);
- gpr_slice s = gpr_slice_from_copied_buffer(
+ grpc_slice s = grpc_slice_from_copied_buffer(
buf.get(), payload_config.bytebuf_params().req_size());
Slice slice(s, Slice::STEAL_REF);
*req = ByteBuffer(&slice, 1);
@@ -112,21 +112,39 @@ class ClientRequestCreator<ByteBuffer> {
}
};
-class HistogramEntry GRPC_FINAL {
+class HistogramEntry final {
public:
- HistogramEntry() : used_(false) {}
- bool used() const { return used_; }
+ HistogramEntry() : value_used_(false), status_used_(false) {}
+ bool value_used() const { return value_used_; }
double value() const { return value_; }
void set_value(double v) {
- used_ = true;
+ value_used_ = true;
value_ = v;
}
+ bool status_used() const { return status_used_; }
+ int status() const { return status_; }
+ void set_status(int status) {
+ status_used_ = true;
+ status_ = status;
+ }
private:
- bool used_;
+ bool value_used_;
double value_;
+ bool status_used_;
+ int status_;
};
+typedef std::unordered_map<int, int64_t> StatusHistogram;
+
+inline void MergeStatusHistogram(const StatusHistogram& from,
+ StatusHistogram* to) {
+ for (StatusHistogram::const_iterator it = from.begin(); it != from.end();
+ ++it) {
+ (*to)[it->first] += it->second;
+ }
+}
+
class Client {
public:
Client()
@@ -139,34 +157,41 @@ class Client {
ClientStats Mark(bool reset) {
Histogram latencies;
+ StatusHistogram statuses;
UsageTimer::Result timer_result;
MaybeStartRequests();
- // avoid std::vector for old compilers that expect a copy constructor
if (reset) {
- Histogram* to_merge = new Histogram[threads_.size()];
+ std::vector<Histogram> to_merge(threads_.size());
+ std::vector<StatusHistogram> to_merge_status(threads_.size());
+
for (size_t i = 0; i < threads_.size(); i++) {
- threads_[i]->BeginSwap(&to_merge[i]);
+ threads_[i]->BeginSwap(&to_merge[i], &to_merge_status[i]);
}
std::unique_ptr<UsageTimer> timer(new UsageTimer);
timer_.swap(timer);
for (size_t i = 0; i < threads_.size(); i++) {
- threads_[i]->EndSwap();
latencies.Merge(to_merge[i]);
+ MergeStatusHistogram(to_merge_status[i], &statuses);
}
- delete[] to_merge;
timer_result = timer->Mark();
} else {
// merge snapshots of each thread histogram
for (size_t i = 0; i < threads_.size(); i++) {
- threads_[i]->MergeStatsInto(&latencies);
+ threads_[i]->MergeStatsInto(&latencies, &statuses);
}
timer_result = timer_->Mark();
}
ClientStats stats;
latencies.FillProto(stats.mutable_latencies());
+ for (StatusHistogram::const_iterator it = statuses.begin();
+ it != statuses.end(); ++it) {
+ RequestResultCount* rrc = stats.add_request_results();
+ rrc->set_status_code(it->first);
+ rrc->set_count(it->second);
+ }
stats.set_time_elapsed(timer_result.wall);
stats.set_time_system(timer_result.system);
stats.set_time_user(timer_result.user);
@@ -258,16 +283,16 @@ class Client {
~Thread() { impl_.join(); }
- void BeginSwap(Histogram* n) {
+ void BeginSwap(Histogram* n, StatusHistogram* s) {
std::lock_guard<std::mutex> g(mu_);
n->Swap(&histogram_);
+ s->swap(statuses_);
}
- void EndSwap() {}
-
- void MergeStatsInto(Histogram* hist) {
+ void MergeStatsInto(Histogram* hist, StatusHistogram* s) {
std::unique_lock<std::mutex> g(mu_);
hist->Merge(histogram_);
+ MergeStatusHistogram(statuses_, s);
}
private:
@@ -288,9 +313,12 @@ class Client {
const bool thread_still_ok = client_->ThreadFunc(&entry, idx_);
// lock, update histogram if needed and see if we're done
std::lock_guard<std::mutex> g(mu_);
- if (entry.used()) {
+ if (entry.value_used()) {
histogram_.Add(entry.value());
}
+ if (entry.status_used()) {
+ statuses_[entry.status()]++;
+ }
if (!thread_still_ok) {
gpr_log(GPR_ERROR, "Finishing client thread due to RPC error");
}
@@ -304,6 +332,7 @@ class Client {
std::mutex mu_;
Histogram histogram_;
+ StatusHistogram statuses_;
Client* client_;
const size_t idx_;
std::thread impl_;
@@ -344,7 +373,7 @@ class ClientImpl : public Client {
ClientImpl(const ClientConfig& config,
std::function<std::unique_ptr<StubType>(std::shared_ptr<Channel>)>
create_stub)
- : cores_(LimitCores(config.core_list().data(), config.core_list_size())),
+ : cores_(gpr_cpu_num_cores()),
channels_(config.client_channels()),
create_stub_(create_stub) {
for (int i = 0; i < config.client_channels(); i++) {
@@ -379,6 +408,7 @@ class ClientImpl : public Client {
// old compilers happy with using this in std::vector
ChannelArguments args;
args.SetInt("shard_to_ensure_no_subchannel_merges", shard);
+ set_channel_args(config, &args);
channel_ = CreateTestChannel(
target, config.security_params().server_host_override(),
config.has_security_params(), !config.security_params().use_test_ca(),
@@ -393,6 +423,18 @@ class ClientImpl : public Client {
StubType* get_stub() { return stub_.get(); }
private:
+ void set_channel_args(const ClientConfig& config, ChannelArguments* args) {
+ for (auto channel_arg : config.channel_args()) {
+ if (channel_arg.value_case() == ChannelArg::kStrValue) {
+ args->SetString(channel_arg.name(), channel_arg.str_value());
+ } else if (channel_arg.value_case() == ChannelArg::kIntValue) {
+ args->SetInt(channel_arg.name(), channel_arg.int_value());
+ } else {
+ gpr_log(GPR_ERROR, "Empty channel arg value.");
+ }
+ }
+ }
+
std::shared_ptr<Channel> channel_;
std::unique_ptr<StubType> stub_;
};
diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc
index 081114859c..4032039ea1 100644
--- a/test/cpp/qps/client_async.cc
+++ b/test/cpp/qps/client_async.cc
@@ -83,7 +83,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
CompletionQueue*)>
start_req,
- std::function<void(grpc::Status, ResponseType*)> on_done)
+ std::function<void(grpc::Status, ResponseType*, HistogramEntry*)> on_done)
: context_(),
stub_(stub),
cq_(nullptr),
@@ -93,8 +93,8 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
callback_(on_done),
next_issue_(next_issue),
start_req_(start_req) {}
- ~ClientRpcContextUnaryImpl() GRPC_OVERRIDE {}
- void Start(CompletionQueue* cq) GRPC_OVERRIDE {
+ ~ClientRpcContextUnaryImpl() override {}
+ void Start(CompletionQueue* cq) override {
cq_ = cq;
if (!next_issue_) { // ready to issue
RunNextState(true, nullptr);
@@ -102,7 +102,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
alarm_.reset(new Alarm(cq_, next_issue_(), ClientRpcContext::tag(this)));
}
}
- bool RunNextState(bool ok, HistogramEntry* entry) GRPC_OVERRIDE {
+ bool RunNextState(bool ok, HistogramEntry* entry) override {
switch (next_state_) {
case State::READY:
start_ = UsageTimer::Now();
@@ -113,7 +113,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
return true;
case State::RESP_DONE:
entry->set_value((UsageTimer::Now() - start_) * 1e9);
- callback_(status_, &response_);
+ callback_(status_, &response_, entry);
next_state_ = State::INVALID;
return false;
default:
@@ -121,7 +121,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
return false;
}
}
- ClientRpcContext* StartNewClone() GRPC_OVERRIDE {
+ ClientRpcContext* StartNewClone() override {
return new ClientRpcContextUnaryImpl(stub_, req_, next_issue_, start_req_,
callback_);
}
@@ -135,7 +135,7 @@ class ClientRpcContextUnaryImpl : public ClientRpcContext {
ResponseType response_;
enum State { INVALID, READY, RESP_DONE };
State next_state_;
- std::function<void(grpc::Status, ResponseType*)> callback_;
+ std::function<void(grpc::Status, ResponseType*, HistogramEntry*)> callback_;
std::function<gpr_timespec()> next_issue_;
std::function<std::unique_ptr<grpc::ClientAsyncResponseReader<ResponseType>>(
BenchmarkService::Stub*, grpc::ClientContext*, const RequestType&,
@@ -177,7 +177,6 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
shutdown_state_.emplace_back(new PerThreadShutdownState());
}
- using namespace std::placeholders;
int t = 0;
for (int ch = 0; ch < config.client_channels(); ch++) {
for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) {
@@ -217,7 +216,7 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
}
return num_threads;
}
- void DestroyMultithreading() GRPC_OVERRIDE GRPC_FINAL {
+ void DestroyMultithreading() override final {
for (auto ss = shutdown_state_.begin(); ss != shutdown_state_.end(); ++ss) {
std::lock_guard<std::mutex> lock((*ss)->mutex);
(*ss)->shutdown = true;
@@ -228,8 +227,7 @@ class AsyncClient : public ClientImpl<StubType, RequestType> {
this->EndThreads(); // this needed for resolution
}
- bool ThreadFunc(HistogramEntry* entry,
- size_t thread_idx) GRPC_OVERRIDE GRPC_FINAL {
+ bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override final {
void* got_tag;
bool ok;
@@ -279,7 +277,7 @@ static std::unique_ptr<BenchmarkService::Stub> BenchmarkStubCreator(
return BenchmarkService::NewStub(ch);
}
-class AsyncUnaryClient GRPC_FINAL
+class AsyncUnaryClient final
: public AsyncClient<BenchmarkService::Stub, SimpleRequest> {
public:
explicit AsyncUnaryClient(const ClientConfig& config)
@@ -287,10 +285,13 @@ class AsyncUnaryClient GRPC_FINAL
config, SetupCtx, BenchmarkStubCreator) {
StartThreads(num_async_threads_);
}
- ~AsyncUnaryClient() GRPC_OVERRIDE {}
+ ~AsyncUnaryClient() override {}
private:
- static void CheckDone(grpc::Status s, SimpleResponse* response) {}
+ static void CheckDone(grpc::Status s, SimpleResponse* response,
+ HistogramEntry* entry) {
+ entry->set_status(s.error_code());
+ }
static std::unique_ptr<grpc::ClientAsyncResponseReader<SimpleResponse>>
StartReq(BenchmarkService::Stub* stub, grpc::ClientContext* ctx,
const SimpleRequest& request, CompletionQueue* cq) {
@@ -326,13 +327,13 @@ class ClientRpcContextStreamingImpl : public ClientRpcContext {
callback_(on_done),
next_issue_(next_issue),
start_req_(start_req) {}
- ~ClientRpcContextStreamingImpl() GRPC_OVERRIDE {}
- void Start(CompletionQueue* cq) GRPC_OVERRIDE {
+ ~ClientRpcContextStreamingImpl() override {}
+ void Start(CompletionQueue* cq) override {
cq_ = cq;
stream_ = start_req_(stub_, &context_, cq, ClientRpcContext::tag(this));
next_state_ = State::STREAM_IDLE;
}
- bool RunNextState(bool ok, HistogramEntry* entry) GRPC_OVERRIDE {
+ bool RunNextState(bool ok, HistogramEntry* entry) override {
while (true) {
switch (next_state_) {
case State::STREAM_IDLE:
@@ -374,7 +375,7 @@ class ClientRpcContextStreamingImpl : public ClientRpcContext {
}
}
}
- ClientRpcContext* StartNewClone() GRPC_OVERRIDE {
+ ClientRpcContext* StartNewClone() override {
return new ClientRpcContextStreamingImpl(stub_, req_, next_issue_,
start_req_, callback_);
}
@@ -407,7 +408,7 @@ class ClientRpcContextStreamingImpl : public ClientRpcContext {
stream_;
};
-class AsyncStreamingClient GRPC_FINAL
+class AsyncStreamingClient final
: public AsyncClient<BenchmarkService::Stub, SimpleRequest> {
public:
explicit AsyncStreamingClient(const ClientConfig& config)
@@ -416,7 +417,7 @@ class AsyncStreamingClient GRPC_FINAL
StartThreads(num_async_threads_);
}
- ~AsyncStreamingClient() GRPC_OVERRIDE {}
+ ~AsyncStreamingClient() override {}
private:
static void CheckDone(grpc::Status s, SimpleResponse* response) {}
@@ -455,8 +456,8 @@ class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
callback_(on_done),
next_issue_(next_issue),
start_req_(start_req) {}
- ~ClientRpcContextGenericStreamingImpl() GRPC_OVERRIDE {}
- void Start(CompletionQueue* cq) GRPC_OVERRIDE {
+ ~ClientRpcContextGenericStreamingImpl() override {}
+ void Start(CompletionQueue* cq) override {
cq_ = cq;
const grpc::string kMethodName(
"/grpc.testing.BenchmarkService/StreamingCall");
@@ -464,7 +465,7 @@ class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
ClientRpcContext::tag(this));
next_state_ = State::STREAM_IDLE;
}
- bool RunNextState(bool ok, HistogramEntry* entry) GRPC_OVERRIDE {
+ bool RunNextState(bool ok, HistogramEntry* entry) override {
while (true) {
switch (next_state_) {
case State::STREAM_IDLE:
@@ -506,7 +507,7 @@ class ClientRpcContextGenericStreamingImpl : public ClientRpcContext {
}
}
}
- ClientRpcContext* StartNewClone() GRPC_OVERRIDE {
+ ClientRpcContext* StartNewClone() override {
return new ClientRpcContextGenericStreamingImpl(stub_, req_, next_issue_,
start_req_, callback_);
}
@@ -543,7 +544,7 @@ static std::unique_ptr<grpc::GenericStub> GenericStubCreator(
return std::unique_ptr<grpc::GenericStub>(new grpc::GenericStub(ch));
}
-class GenericAsyncStreamingClient GRPC_FINAL
+class GenericAsyncStreamingClient final
: public AsyncClient<grpc::GenericStub, ByteBuffer> {
public:
explicit GenericAsyncStreamingClient(const ClientConfig& config)
@@ -552,7 +553,7 @@ class GenericAsyncStreamingClient GRPC_FINAL
StartThreads(num_async_threads_);
}
- ~GenericAsyncStreamingClient() GRPC_OVERRIDE {}
+ ~GenericAsyncStreamingClient() override {}
private:
static void CheckDone(grpc::Status s, ByteBuffer* response) {}
diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc
index 8062424a1f..498416c64a 100644
--- a/test/cpp/qps/client_sync.cc
+++ b/test/cpp/qps/client_sync.cc
@@ -108,10 +108,10 @@ class SynchronousClient
std::vector<SimpleResponse> responses_;
private:
- void DestroyMultithreading() GRPC_OVERRIDE GRPC_FINAL { EndThreads(); }
+ void DestroyMultithreading() override final { EndThreads(); }
};
-class SynchronousUnaryClient GRPC_FINAL : public SynchronousClient {
+class SynchronousUnaryClient final : public SynchronousClient {
public:
SynchronousUnaryClient(const ClientConfig& config)
: SynchronousClient(config) {
@@ -119,7 +119,7 @@ class SynchronousUnaryClient GRPC_FINAL : public SynchronousClient {
}
~SynchronousUnaryClient() {}
- bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) GRPC_OVERRIDE {
+ bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
if (!WaitToIssue(thread_idx)) {
return true;
}
@@ -130,17 +130,17 @@ class SynchronousUnaryClient GRPC_FINAL : public SynchronousClient {
grpc::Status s =
stub->UnaryCall(&context, request_, &responses_[thread_idx]);
entry->set_value((UsageTimer::Now() - start) * 1e9);
- return s.ok();
+ entry->set_status(s.error_code());
+ return true;
}
};
-class SynchronousStreamingClient GRPC_FINAL : public SynchronousClient {
+class SynchronousStreamingClient final : public SynchronousClient {
public:
SynchronousStreamingClient(const ClientConfig& config)
- : SynchronousClient(config) {
- context_ = new grpc::ClientContext[num_threads_];
- stream_ = new std::unique_ptr<
- grpc::ClientReaderWriter<SimpleRequest, SimpleResponse>>[num_threads_];
+ : SynchronousClient(config),
+ context_(num_threads_),
+ stream_(num_threads_) {
for (size_t thread_idx = 0; thread_idx < num_threads_; thread_idx++) {
auto* stub = channels_[thread_idx % channels_.size()].get_stub();
stream_[thread_idx] = stub->StreamingCall(&context_[thread_idx]);
@@ -153,18 +153,15 @@ class SynchronousStreamingClient GRPC_FINAL : public SynchronousClient {
if (*stream) {
(*stream)->WritesDone();
Status s = (*stream)->Finish();
- EXPECT_TRUE(s.ok());
if (!s.ok()) {
gpr_log(GPR_ERROR, "Stream %zu received an error %s", i,
s.error_message().c_str());
}
}
}
- delete[] stream_;
- delete[] context_;
}
- bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) GRPC_OVERRIDE {
+ bool ThreadFunc(HistogramEntry* entry, size_t thread_idx) override {
if (!WaitToIssue(thread_idx)) {
return true;
}
@@ -175,14 +172,19 @@ class SynchronousStreamingClient GRPC_FINAL : public SynchronousClient {
entry->set_value((UsageTimer::Now() - start) * 1e9);
return true;
}
- return false;
+ auto* stub = channels_[thread_idx % channels_.size()].get_stub();
+ context_[thread_idx].~ClientContext();
+ new (&context_[thread_idx]) ClientContext();
+ stream_[thread_idx] = stub->StreamingCall(&context_[thread_idx]);
+ return true;
}
private:
// These are both conceptually std::vector but cannot be for old compilers
// that expect contained classes to support copy constructors
- grpc::ClientContext* context_;
- std::unique_ptr<grpc::ClientReaderWriter<SimpleRequest, SimpleResponse>>*
+ std::vector<grpc::ClientContext> context_;
+ std::vector<
+ std::unique_ptr<grpc::ClientReaderWriter<SimpleRequest, SimpleResponse>>>
stream_;
};
diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc
index 7460bb526a..74fe3662c1 100644
--- a/test/cpp/qps/driver.cc
+++ b/test/cpp/qps/driver.cc
@@ -44,7 +44,9 @@
#include <grpc/support/alloc.h>
#include <grpc/support/host_port.h>
#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+#include "src/core/lib/profiling/timers.h"
#include "src/core/lib/support/env.h"
#include "src/proto/grpc/testing/services.grpc.pb.h"
#include "test/core/util/port.h"
@@ -74,47 +76,36 @@ static std::string get_host(const std::string& worker) {
return s;
}
-static std::unordered_map<string, std::deque<int>> get_hosts_and_cores(
- const deque<string>& workers) {
- std::unordered_map<string, std::deque<int>> hosts;
- for (auto it = workers.begin(); it != workers.end(); it++) {
- const string host = get_host(*it);
- if (hosts.find(host) == hosts.end()) {
- auto stub = WorkerService::NewStub(
- CreateChannel(*it, InsecureChannelCredentials()));
- grpc::ClientContext ctx;
- ctx.set_wait_for_ready(true);
- CoreRequest dummy;
- CoreResponse cores;
- grpc::Status s = stub->CoreCount(&ctx, dummy, &cores);
- GPR_ASSERT(s.ok());
- std::deque<int> dq;
- for (int i = 0; i < cores.cores(); i++) {
- dq.push_back(i);
- }
- hosts[host] = dq;
- }
+static deque<string> get_workers(const string& env_name) {
+ char* env = gpr_getenv(env_name.c_str());
+ if (!env) {
+ env = gpr_strdup("");
}
- return hosts;
-}
-
-static deque<string> get_workers(const string& name) {
- char* env = gpr_getenv(name.c_str());
- if (!env) return deque<string>();
-
deque<string> out;
char* p = env;
- for (;;) {
- char* comma = strchr(p, ',');
- if (comma) {
- out.emplace_back(p, comma);
- p = comma + 1;
- } else {
- out.emplace_back(p);
- gpr_free(env);
- return out;
+ if (strlen(env) != 0) {
+ for (;;) {
+ char* comma = strchr(p, ',');
+ if (comma) {
+ out.emplace_back(p, comma);
+ p = comma + 1;
+ } else {
+ out.emplace_back(p);
+ break;
+ }
}
}
+ if (out.size() == 0) {
+ gpr_log(GPR_ERROR,
+ "Environment variable \"%s\" does not contain a list of QPS "
+ "workers to use. Set it to a comma-separated list of "
+ "hostname:port pairs, starting with hosts that should act as "
+ "servers. E.g. export "
+ "%s=\"serverhost1:1234,clienthost1:1234,clienthost2:1234\"",
+ env_name.c_str(), env_name.c_str());
+ }
+ gpr_free(env);
+ return out;
}
// helpers for postprocess_scenario_result
@@ -124,6 +115,8 @@ static double UserTime(ClientStats s) { return s.time_user(); }
static double ServerWallTime(ServerStats s) { return s.time_elapsed(); }
static double ServerSystemTime(ServerStats s) { return s.time_system(); }
static double ServerUserTime(ServerStats s) { return s.time_user(); }
+static double ServerTotalCpuTime(ServerStats s) { return s.total_cpu_time(); }
+static double ServerIdleCpuTime(ServerStats s) { return s.idle_cpu_time(); }
static int Cores(int n) { return n; }
// Postprocess ScenarioResult and populate result summary.
@@ -131,7 +124,8 @@ static void postprocess_scenario_result(ScenarioResult* result) {
Histogram histogram;
histogram.MergeProto(result->latencies());
- auto qps = histogram.Count() / average(result->client_stats(), WallTime);
+ auto time_estimate = average(result->client_stats(), WallTime);
+ auto qps = histogram.Count() / time_estimate;
auto qps_per_server_core = qps / sum(result->server_cores(), Cores);
result->mutable_summary()->set_qps(qps);
@@ -147,6 +141,7 @@ static void postprocess_scenario_result(ScenarioResult* result) {
sum(result->server_stats(), ServerWallTime);
auto server_user_time = 100.0 * sum(result->server_stats(), ServerUserTime) /
sum(result->server_stats(), ServerWallTime);
+
auto client_system_time = 100.0 * sum(result->client_stats(), SystemTime) /
sum(result->client_stats(), WallTime);
auto client_user_time = 100.0 * sum(result->client_stats(), UserTime) /
@@ -156,41 +151,53 @@ static void postprocess_scenario_result(ScenarioResult* result) {
result->mutable_summary()->set_server_user_time(server_user_time);
result->mutable_summary()->set_client_system_time(client_system_time);
result->mutable_summary()->set_client_user_time(client_user_time);
-}
-
-// Namespace for classes and functions used only in RunScenario
-// Using this rather than local definitions to workaround gcc-4.4 limitations
-// regarding using templates without linkage
-namespace runsc {
-
-// ClientContext allocator
-static ClientContext* AllocContext(list<ClientContext>* contexts) {
- contexts->emplace_back();
- auto context = &contexts->back();
- context->set_wait_for_ready(true);
- return context;
-}
-struct ServerData {
- unique_ptr<WorkerService::Stub> stub;
- unique_ptr<ClientReaderWriter<ServerArgs, ServerStatus>> stream;
-};
+ // For Non-linux platform, get_cpu_usage() is not implemented. Thus,
+ // ServerTotalCpuTime and ServerIdleCpuTime are both 0.
+ if (average(result->server_stats(), ServerTotalCpuTime) == 0) {
+ result->mutable_summary()->set_server_cpu_usage(0);
+ } else {
+ auto server_cpu_usage =
+ 100 -
+ 100 * average(result->server_stats(), ServerIdleCpuTime) /
+ average(result->server_stats(), ServerTotalCpuTime);
+ result->mutable_summary()->set_server_cpu_usage(server_cpu_usage);
+ }
-struct ClientData {
- unique_ptr<WorkerService::Stub> stub;
- unique_ptr<ClientReaderWriter<ClientArgs, ClientStatus>> stream;
-};
-} // namespace runsc
+ if (result->request_results_size() > 0) {
+ int64_t successes = 0;
+ int64_t failures = 0;
+ for (int i = 0; i < result->request_results_size(); i++) {
+ RequestResultCount rrc = result->request_results(i);
+ if (rrc.status_code() == 0) {
+ successes += rrc.count();
+ } else {
+ failures += rrc.count();
+ }
+ }
+ result->mutable_summary()->set_successful_requests_per_second(
+ successes / time_estimate);
+ result->mutable_summary()->set_failed_requests_per_second(failures /
+ time_estimate);
+ }
+}
std::unique_ptr<ScenarioResult> RunScenario(
const ClientConfig& initial_client_config, size_t num_clients,
const ServerConfig& initial_server_config, size_t num_servers,
- int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count) {
+ int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count,
+ const char* qps_server_target_override) {
// Log everything from the driver
gpr_set_log_verbosity(GPR_LOG_SEVERITY_DEBUG);
// ClientContext allocations (all are destroyed at scope exit)
list<ClientContext> contexts;
+ auto alloc_context = [](list<ClientContext>* contexts) {
+ contexts->emplace_back();
+ auto context = &contexts->back();
+ context->set_wait_for_ready(true);
+ return context;
+ };
// To be added to the result, containing the final configuration used for
// client and config (including host, etc.)
@@ -224,9 +231,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
workers.push_back(addr);
}
}
-
- // Setup the hosts and core counts
- auto hosts_cores = get_hosts_and_cores(workers);
+ GPR_ASSERT(workers.size() != 0);
// if num_clients is set to <=0, do dynamic sizing: all workers
// except for servers are clients
@@ -243,10 +248,13 @@ std::unique_ptr<ScenarioResult> RunScenario(
workers.resize(num_clients + num_servers);
// Start servers
- using runsc::ServerData;
- // servers is array rather than std::vector to avoid gcc-4.4 issues
- // where class contained in std::vector must have a copy constructor
- auto* servers = new ServerData[num_servers];
+ struct ServerData {
+ unique_ptr<WorkerService::Stub> stub;
+ unique_ptr<ClientReaderWriter<ServerArgs, ServerStatus>> stream;
+ };
+ std::vector<ServerData> servers(num_servers);
+ std::unordered_map<string, std::deque<int>> hosts_cores;
+
for (size_t i = 0; i < num_servers; i++) {
gpr_log(GPR_INFO, "Starting server on %s (worker #%" PRIuPTR ")",
workers[i].c_str(), i);
@@ -254,44 +262,14 @@ std::unique_ptr<ScenarioResult> RunScenario(
CreateChannel(workers[i], InsecureChannelCredentials()));
ServerConfig server_config = initial_server_config;
- char* host;
- char* driver_port;
- char* cli_target;
- gpr_split_host_port(workers[i].c_str(), &host, &driver_port);
- string host_str(host);
- int server_core_limit = initial_server_config.core_limit();
- int client_core_limit = initial_client_config.core_limit();
-
- if (server_core_limit == 0 && client_core_limit > 0) {
- // In this case, limit the server cores if it matches the
- // same host as one or more clients
- const auto& dq = hosts_cores.at(host_str);
- bool match = false;
- int limit = dq.size();
- for (size_t cli = 0; cli < num_clients; cli++) {
- if (host_str == get_host(workers[cli + num_servers])) {
- limit -= client_core_limit;
- match = true;
- }
- }
- if (match) {
- GPR_ASSERT(limit > 0);
- server_core_limit = limit;
- }
- }
- if (server_core_limit > 0) {
- auto& dq = hosts_cores.at(host_str);
- GPR_ASSERT(dq.size() >= static_cast<size_t>(server_core_limit));
- for (int core = 0; core < server_core_limit; core++) {
- server_config.add_core_list(dq.front());
- dq.pop_front();
- }
+ if (server_config.core_limit() != 0) {
+ gpr_log(GPR_ERROR,
+ "server config core limit is set but ignored by driver");
}
ServerArgs args;
*args.mutable_setup() = server_config;
- servers[i].stream =
- servers[i].stub->RunServer(runsc::AllocContext(&contexts));
+ servers[i].stream = servers[i].stub->RunServer(alloc_context(&contexts));
if (!servers[i].stream->Write(args)) {
gpr_log(GPR_ERROR, "Could not write args to server %zu", i);
}
@@ -299,20 +277,29 @@ std::unique_ptr<ScenarioResult> RunScenario(
if (!servers[i].stream->Read(&init_status)) {
gpr_log(GPR_ERROR, "Server %zu did not yield initial status", i);
}
- gpr_join_host_port(&cli_target, host, init_status.port());
- client_config.add_server_targets(cli_target);
- gpr_free(host);
- gpr_free(driver_port);
- gpr_free(cli_target);
+ if (qps_server_target_override != NULL &&
+ strlen(qps_server_target_override) > 0) {
+ // overriding the qps server target only works if there is 1 server
+ GPR_ASSERT(num_servers == 1);
+ client_config.add_server_targets(qps_server_target_override);
+ } else {
+ std::string host;
+ char* cli_target;
+ host = get_host(workers[i]);
+ gpr_join_host_port(&cli_target, host.c_str(), init_status.port());
+ client_config.add_server_targets(cli_target);
+ gpr_free(cli_target);
+ }
}
// Targets are all set by now
result_client_config = client_config;
// Start clients
- using runsc::ClientData;
- // 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];
+ struct ClientData {
+ unique_ptr<WorkerService::Stub> stub;
+ unique_ptr<ClientReaderWriter<ClientArgs, ClientStatus>> stream;
+ };
+ std::vector<ClientData> clients(num_clients);
size_t channels_allocated = 0;
for (size_t i = 0; i < num_clients; i++) {
const auto& worker = workers[i + num_servers];
@@ -322,31 +309,8 @@ std::unique_ptr<ScenarioResult> RunScenario(
CreateChannel(worker, InsecureChannelCredentials()));
ClientConfig per_client_config = client_config;
- int server_core_limit = initial_server_config.core_limit();
- int client_core_limit = initial_client_config.core_limit();
- if ((server_core_limit > 0) || (client_core_limit > 0)) {
- auto& dq = hosts_cores.at(get_host(worker));
- if (client_core_limit == 0) {
- // limit client cores if it matches a server host
- bool match = false;
- int limit = dq.size();
- for (size_t srv = 0; srv < num_servers; srv++) {
- if (get_host(worker) == get_host(workers[srv])) {
- match = true;
- }
- }
- if (match) {
- GPR_ASSERT(limit > 0);
- client_core_limit = limit;
- }
- }
- if (client_core_limit > 0) {
- GPR_ASSERT(dq.size() >= static_cast<size_t>(client_core_limit));
- for (int core = 0; core < client_core_limit; core++) {
- per_client_config.add_core_list(dq.front());
- dq.pop_front();
- }
- }
+ if (initial_client_config.core_limit() != 0) {
+ gpr_log(GPR_ERROR, "client config core limit set but ignored");
}
// Reduce channel count so that total channels specified is held regardless
@@ -361,8 +325,7 @@ std::unique_ptr<ScenarioResult> RunScenario(
ClientArgs args;
*args.mutable_setup() = per_client_config;
- clients[i].stream =
- clients[i].stub->RunClient(runsc::AllocContext(&contexts));
+ clients[i].stream = clients[i].stub->RunClient(alloc_context(&contexts));
if (!clients[i].stream->Write(args)) {
gpr_log(GPR_ERROR, "Could not write args to client %zu", i);
}
@@ -438,9 +401,12 @@ std::unique_ptr<ScenarioResult> RunScenario(
start,
gpr_time_from_seconds(warmup_seconds + benchmark_seconds, GPR_TIMESPAN)));
+ gpr_timer_set_enabled(0);
+
// Finish a run
std::unique_ptr<ScenarioResult> result(new ScenarioResult);
Histogram merged_latencies;
+ std::unordered_map<int, int64_t> merged_statuses;
gpr_log(GPR_INFO, "Finishing clients");
for (size_t i = 0; i < num_clients; i++) {
@@ -459,6 +425,10 @@ std::unique_ptr<ScenarioResult> RunScenario(
gpr_log(GPR_INFO, "Received final status from client %zu", i);
const auto& stats = client_status.stats();
merged_latencies.MergeProto(stats.latencies());
+ for (int i = 0; i < stats.request_results_size(); i++) {
+ merged_statuses[stats.request_results(i).status_code()] +=
+ stats.request_results(i).count();
+ }
result->add_client_stats()->CopyFrom(stats);
// That final status should be the last message on the client stream
GPR_ASSERT(!client->stream->Read(&client_status));
@@ -475,9 +445,14 @@ std::unique_ptr<ScenarioResult> RunScenario(
s.error_message().c_str());
}
}
- delete[] clients;
merged_latencies.FillProto(result->mutable_latencies());
+ for (std::unordered_map<int, int64_t>::iterator it = merged_statuses.begin();
+ it != merged_statuses.end(); ++it) {
+ RequestResultCount* rrc = result->add_request_results();
+ rrc->set_status_code(it->first);
+ rrc->set_count(it->second);
+ }
gpr_log(GPR_INFO, "Finishing servers");
for (size_t i = 0; i < num_servers; i++) {
@@ -512,8 +487,6 @@ std::unique_ptr<ScenarioResult> RunScenario(
}
}
- delete[] servers;
-
postprocess_scenario_result(result.get());
return result;
}
@@ -522,6 +495,9 @@ bool RunQuit() {
// Get client, server lists
bool result = true;
auto workers = get_workers("QPS_WORKERS");
+ if (workers.size() == 0) {
+ return false;
+ }
for (size_t i = 0; i < workers.size(); i++) {
auto stub = WorkerService::NewStub(
CreateChannel(workers[i], InsecureChannelCredentials()));
diff --git a/test/cpp/qps/driver.h b/test/cpp/qps/driver.h
index 93f4370caf..e72d30a4ef 100644
--- a/test/cpp/qps/driver.h
+++ b/test/cpp/qps/driver.h
@@ -45,7 +45,8 @@ namespace testing {
std::unique_ptr<ScenarioResult> RunScenario(
const grpc::testing::ClientConfig& client_config, size_t num_clients,
const grpc::testing::ServerConfig& server_config, size_t num_servers,
- int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count);
+ int warmup_seconds, int benchmark_seconds, int spawn_local_worker_count,
+ const char* qps_server_target_override = "");
bool RunQuit();
} // namespace testing
diff --git a/test/cpp/qps/gen_build_yaml.py b/test/cpp/qps/gen_build_yaml.py
index 369da2c8ca..2f035abedd 100755
--- a/test/cpp/qps/gen_build_yaml.py
+++ b/test/cpp/qps/gen_build_yaml.py
@@ -43,28 +43,38 @@ sys.path.append(run_tests_root)
import performance.scenario_config as scenario_config
-def _scenario_json_string(scenario_json):
+configs_from_yaml = yaml.load(open(os.path.join(os.path.dirname(sys.argv[0]), '../../../build.yaml')))['configs'].keys()
+
+def mutate_scenario(scenario_json, is_tsan):
# tweak parameters to get fast test times
+ scenario_json = dict(scenario_json)
scenario_json['warmup_seconds'] = 0
scenario_json['benchmark_seconds'] = 1
- scenarios_json = {'scenarios': [scenario_config.remove_nonproto_fields(scenario_json)]}
+ outstanding_rpcs_divisor = 1
+ if is_tsan and (
+ scenario_json['client_config']['client_type'] == 'SYNC_CLIENT' or
+ scenario_json['server_config']['server_type'] == 'SYNC_SERVER'):
+ outstanding_rpcs_divisor = 10
+ scenario_json['client_config']['outstanding_rpcs_per_channel'] = max(1,
+ int(scenario_json['client_config']['outstanding_rpcs_per_channel'] / outstanding_rpcs_divisor))
+ return scenario_json
+
+def _scenario_json_string(scenario_json, is_tsan):
+ scenarios_json = {'scenarios': [scenario_config.remove_nonproto_fields(mutate_scenario(scenario_json, is_tsan))]}
return json.dumps(scenarios_json)
-def threads_of_type(scenario_json, path):
- d = scenario_json
- for el in path.split('/'):
- if el not in d:
- return 0
- d = d[el]
- return d
+def threads_required(scenario_json, where, is_tsan):
+ scenario_json = mutate_scenario(scenario_json, is_tsan)
+ if scenario_json['%s_config' % where]['%s_type' % where] == 'ASYNC_%s' % where.upper():
+ return scenario_json['%s_config' % where].get('async_%s_threads' % where, 0)
+ return scenario_json['client_config']['outstanding_rpcs_per_channel'] * scenario_json['client_config']['client_channels']
-def guess_cpu(scenario_json):
- client = threads_of_type(scenario_json, 'client_config/async_client_threads')
- server = threads_of_type(scenario_json, 'server_config/async_server_threads')
+def guess_cpu(scenario_json, is_tsan):
+ client = threads_required(scenario_json, 'client', is_tsan)
+ server = threads_required(scenario_json, 'server', is_tsan)
# make an arbitrary guess if set to auto-detect
# about the size of the jenkins instances we have for unit tests
- if client == 0: client = 8
- if server == 0: server = 8
+ if client == 0 or server == 0: return 'capacity'
return (scenario_json['num_clients'] * client +
scenario_json['num_servers'] * server)
@@ -73,18 +83,37 @@ print yaml.dump({
{
'name': 'json_run_localhost',
'shortname': 'json_run_localhost:%s' % scenario_json['name'],
- 'args': ['--scenarios_json', _scenario_json_string(scenario_json)],
+ 'args': ['--scenarios_json', _scenario_json_string(scenario_json, False)],
'ci_platforms': ['linux'],
'platforms': ['linux'],
'flaky': False,
'language': 'c++',
'boringssl': True,
'defaults': 'boringssl',
- 'cpu_cost': guess_cpu(scenario_json),
- 'exclude_configs': [],
- 'timeout_seconds': 3*60
+ 'cpu_cost': guess_cpu(scenario_json, False),
+ 'exclude_configs': ['tsan', 'asan'],
+ 'timeout_seconds': 6*60,
+ 'excluded_poll_engines': scenario_json.get('EXCLUDED_POLL_ENGINES', [])
}
for scenario_json in scenario_config.CXXLanguage().scenarios()
if 'scalable' in scenario_json.get('CATEGORIES', [])
+ ] + [
+ {
+ 'name': 'json_run_localhost',
+ 'shortname': 'json_run_localhost:%s_low_thread_count' % scenario_json['name'],
+ 'args': ['--scenarios_json', _scenario_json_string(scenario_json, True)],
+ 'ci_platforms': ['linux'],
+ 'platforms': ['linux'],
+ 'flaky': False,
+ 'language': 'c++',
+ 'boringssl': True,
+ 'defaults': 'boringssl',
+ 'cpu_cost': guess_cpu(scenario_json, True),
+ 'exclude_configs': sorted(c for c in configs_from_yaml if c not in ('tsan', 'asan')),
+ 'timeout_seconds': 6*60,
+ 'excluded_poll_engines': scenario_json.get('EXCLUDED_POLL_ENGINES', [])
+ }
+ for scenario_json in scenario_config.CXXLanguage().scenarios()
+ if 'scalable' in scenario_json.get('CATEGORIES', [])
]
})
diff --git a/test/cpp/qps/interarrival.h b/test/cpp/qps/interarrival.h
index 0980d5e8ba..4bef06f566 100644
--- a/test/cpp/qps/interarrival.h
+++ b/test/cpp/qps/interarrival.h
@@ -69,11 +69,11 @@ inline RandomDistInterface::~RandomDistInterface() {}
// independent identical stationary sources. For more information,
// see http://en.wikipedia.org/wiki/Exponential_distribution
-class ExpDist GRPC_FINAL : public RandomDistInterface {
+class ExpDist final : public RandomDistInterface {
public:
explicit ExpDist(double lambda) : lambda_recip_(1.0 / lambda) {}
- ~ExpDist() GRPC_OVERRIDE {}
- double transform(double uni) const GRPC_OVERRIDE {
+ ~ExpDist() override {}
+ double transform(double uni) const override {
// Note: Use 1.0-uni above to avoid NaN if uni is 0
return lambda_recip_ * (-log(1.0 - uni));
}
diff --git a/test/cpp/qps/json_run_localhost.cc b/test/cpp/qps/json_run_localhost.cc
index 74e40fbf1a..b7b2553f12 100644
--- a/test/cpp/qps/json_run_localhost.cc
+++ b/test/cpp/qps/json_run_localhost.cc
@@ -31,7 +31,11 @@
*
*/
+#include <signal.h>
+#include <string.h>
+
#include <memory>
+#include <mutex>
#include <sstream>
#include <string>
@@ -43,6 +47,11 @@
using grpc::SubProcess;
+constexpr auto kNumWorkers = 2;
+
+static SubProcess* g_driver;
+static SubProcess* g_workers[kNumWorkers];
+
template <class T>
std::string as_string(const T& val) {
std::ostringstream out;
@@ -50,9 +59,38 @@ std::string as_string(const T& val) {
return out.str();
}
+static void sighandler(int sig) {
+ const int errno_saved = errno;
+ if (g_driver != NULL) g_driver->Interrupt();
+ for (int i = 0; i < kNumWorkers; ++i) {
+ if (g_workers[i]) g_workers[i]->Interrupt();
+ }
+ errno = errno_saved;
+}
+
+static void register_sighandler() {
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sighandler;
+
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+}
+
+static void LogStatus(int status, const char* label) {
+ if (WIFEXITED(status)) {
+ gpr_log(GPR_INFO, "%s: subprocess exited with status %d", label,
+ WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ gpr_log(GPR_INFO, "%s: subprocess terminated with signal %d", label,
+ WTERMSIG(status));
+ } else {
+ gpr_log(GPR_INFO, "%s: unknown subprocess status: %d", label, status);
+ }
+}
+
int main(int argc, char** argv) {
- typedef std::unique_ptr<SubProcess> SubProcessPtr;
- std::vector<SubProcessPtr> jobs;
+ register_sighandler();
std::string my_bin = argv[0];
std::string bin_dir = my_bin.substr(0, my_bin.rfind('/'));
@@ -60,11 +98,11 @@ int main(int argc, char** argv) {
std::ostringstream env;
bool first = true;
- for (int i = 0; i < 2; i++) {
- auto port = grpc_pick_unused_port_or_die();
+ for (int i = 0; i < kNumWorkers; i++) {
+ const auto port = grpc_pick_unused_port_or_die();
std::vector<std::string> args = {bin_dir + "/qps_worker", "-driver_port",
as_string(port)};
- jobs.emplace_back(new SubProcess(args));
+ g_workers[i] = new SubProcess(args);
if (!first) env << ",";
env << "localhost:" << port;
first = false;
@@ -75,12 +113,27 @@ int main(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
args.push_back(argv[i]);
}
- GPR_ASSERT(SubProcess(args).Join() == 0);
- for (auto it = jobs.begin(); it != jobs.end(); ++it) {
- (*it)->Interrupt();
+ g_driver = new SubProcess(args);
+ const int driver_join_status = g_driver->Join();
+ if (driver_join_status != 0) {
+ LogStatus(driver_join_status, "driver");
}
- for (auto it = jobs.begin(); it != jobs.end(); ++it) {
- (*it)->Join();
+ for (int i = 0; i < kNumWorkers; ++i) {
+ if (g_workers[i]) g_workers[i]->Interrupt();
}
+
+ for (int i = 0; i < kNumWorkers; ++i) {
+ if (g_workers[i]) {
+ const int worker_status = g_workers[i]->Join();
+ if (worker_status != 0) {
+ LogStatus(worker_status, "worker");
+ }
+ }
+ }
+
+ delete g_driver;
+ g_driver = NULL;
+ for (int i = 0; i < kNumWorkers; ++i) delete g_workers[i];
+ GPR_ASSERT(driver_join_status == 0);
}
diff --git a/test/cpp/qps/qps_json_driver.cc b/test/cpp/qps/qps_json_driver.cc
index 1524ebbc38..ddaaa7ca75 100644
--- a/test/cpp/qps/qps_json_driver.cc
+++ b/test/cpp/qps/qps_json_driver.cc
@@ -49,10 +49,116 @@ DEFINE_string(scenarios_file, "",
DEFINE_string(scenarios_json, "",
"JSON string containing an array of Scenario objects");
DEFINE_bool(quit, false, "Quit the workers");
+DEFINE_string(search_param, "",
+ "The parameter, whose value is to be searched for to achieve "
+ "targeted cpu load. For now, we have 'offered_load'. Later, "
+ "'num_channels', 'num_outstanding_requests', etc. shall be "
+ "added.");
+DEFINE_double(
+ initial_search_value, 0.0,
+ "initial parameter value to start the search with (i.e. lower bound)");
+DEFINE_double(targeted_cpu_load, 70.0,
+ "Targeted cpu load (unit: %, range [0,100])");
+DEFINE_double(stride, 1,
+ "Defines each stride of the search. The larger the stride is, "
+ "the coarser the result will be, but will also be faster.");
+DEFINE_double(error_tolerance, 0.01,
+ "Defines threshold for stopping the search. When current search "
+ "range is narrower than the error_tolerance computed range, we "
+ "stop the search.");
+
+DEFINE_string(qps_server_target_override, "",
+ "Override QPS server target to configure in client configs."
+ "Only applicable if there is a single benchmark server.");
namespace grpc {
namespace testing {
+static std::unique_ptr<ScenarioResult> RunAndReport(const Scenario& scenario,
+ bool* success) {
+ std::cerr << "RUNNING SCENARIO: " << scenario.name() << "\n";
+ auto result =
+ RunScenario(scenario.client_config(), scenario.num_clients(),
+ scenario.server_config(), scenario.num_servers(),
+ scenario.warmup_seconds(), scenario.benchmark_seconds(),
+ scenario.spawn_local_worker_count(),
+ FLAGS_qps_server_target_override.c_str());
+
+ // Amend the result with scenario config. Eventually we should adjust
+ // RunScenario contract so we don't need to touch the result here.
+ result->mutable_scenario()->CopyFrom(scenario);
+
+ GetReporter()->ReportQPS(*result);
+ GetReporter()->ReportQPSPerCore(*result);
+ GetReporter()->ReportLatency(*result);
+ GetReporter()->ReportTimes(*result);
+ GetReporter()->ReportCpuUsage(*result);
+
+ for (int i = 0; *success && i < result->client_success_size(); i++) {
+ *success = result->client_success(i);
+ }
+ for (int i = 0; *success && i < result->server_success_size(); i++) {
+ *success = result->server_success(i);
+ }
+
+ return result;
+}
+
+static double GetCpuLoad(Scenario* scenario, double offered_load,
+ bool* success) {
+ scenario->mutable_client_config()
+ ->mutable_load_params()
+ ->mutable_poisson()
+ ->set_offered_load(offered_load);
+ auto result = RunAndReport(*scenario, success);
+ return result->summary().server_cpu_usage();
+}
+
+static double BinarySearch(Scenario* scenario, double targeted_cpu_load,
+ double low, double high, bool* success) {
+ while (low <= high * (1 - FLAGS_error_tolerance)) {
+ double mid = low + (high - low) / 2;
+ double current_cpu_load = GetCpuLoad(scenario, mid, success);
+ gpr_log(GPR_DEBUG, "Binary Search: current_offered_load %.0f", mid);
+ if (!*success) {
+ gpr_log(GPR_ERROR, "Client/Server Failure");
+ break;
+ }
+ if (targeted_cpu_load <= current_cpu_load) {
+ high = mid - FLAGS_stride;
+ } else {
+ low = mid + FLAGS_stride;
+ }
+ }
+
+ return low;
+}
+
+static double SearchOfferedLoad(double initial_offered_load,
+ double targeted_cpu_load, Scenario* scenario,
+ bool* success) {
+ std::cerr << "RUNNING SCENARIO: " << scenario->name() << "\n";
+ double current_offered_load = initial_offered_load;
+ double current_cpu_load = GetCpuLoad(scenario, current_offered_load, success);
+ if (current_cpu_load > targeted_cpu_load) {
+ gpr_log(GPR_ERROR, "Initial offered load too high");
+ return -1;
+ }
+
+ while (*success && (current_cpu_load < targeted_cpu_load)) {
+ current_offered_load *= 2;
+ current_cpu_load = GetCpuLoad(scenario, current_offered_load, success);
+ gpr_log(GPR_DEBUG, "Binary Search: current_offered_load %.0f",
+ current_offered_load);
+ }
+
+ double targeted_offered_load =
+ BinarySearch(scenario, targeted_cpu_load, current_offered_load / 2,
+ current_offered_load, success);
+
+ return targeted_offered_load;
+}
+
static bool QpsDriver() {
grpc::string json;
@@ -68,11 +174,11 @@ static bool QpsDriver() {
if (scfile) {
// Read the json data from disk
- FILE *json_file = fopen(FLAGS_scenarios_file.c_str(), "r");
+ FILE* json_file = fopen(FLAGS_scenarios_file.c_str(), "r");
GPR_ASSERT(json_file != NULL);
fseek(json_file, 0, SEEK_END);
long len = ftell(json_file);
- char *data = new char[len];
+ char* data = new char[len];
fseek(json_file, 0, SEEK_SET);
GPR_ASSERT(len == (long)fread(data, 1, len, json_file));
fclose(json_file);
@@ -93,28 +199,20 @@ static bool QpsDriver() {
GPR_ASSERT(scenarios.scenarios_size() > 0);
for (int i = 0; i < scenarios.scenarios_size(); i++) {
- const Scenario &scenario = scenarios.scenarios(i);
- std::cerr << "RUNNING SCENARIO: " << scenario.name() << "\n";
- auto result =
- RunScenario(scenario.client_config(), scenario.num_clients(),
- scenario.server_config(), scenario.num_servers(),
- scenario.warmup_seconds(), scenario.benchmark_seconds(),
- scenario.spawn_local_worker_count());
-
- // Amend the result with scenario config. Eventually we should adjust
- // RunScenario contract so we don't need to touch the result here.
- result->mutable_scenario()->CopyFrom(scenario);
-
- GetReporter()->ReportQPS(*result);
- GetReporter()->ReportQPSPerCore(*result);
- GetReporter()->ReportLatency(*result);
- GetReporter()->ReportTimes(*result);
-
- for (int i = 0; success && i < result->client_success_size(); i++) {
- success = result->client_success(i);
- }
- for (int i = 0; success && i < result->server_success_size(); i++) {
- success = result->server_success(i);
+ if (FLAGS_search_param == "") {
+ const Scenario& scenario = scenarios.scenarios(i);
+ RunAndReport(scenario, &success);
+ } else {
+ if (FLAGS_search_param == "offered_load") {
+ Scenario* scenario = scenarios.mutable_scenarios(i);
+ double targeted_offered_load =
+ SearchOfferedLoad(FLAGS_initial_search_value,
+ FLAGS_targeted_cpu_load, scenario, &success);
+ gpr_log(GPR_INFO, "targeted_offered_load %f", targeted_offered_load);
+ GetCpuLoad(scenario, targeted_offered_load, &success);
+ } else {
+ gpr_log(GPR_ERROR, "Unimplemented search param");
+ }
}
}
return success;
@@ -123,7 +221,7 @@ static bool QpsDriver() {
} // namespace testing
} // namespace grpc
-int main(int argc, char **argv) {
+int main(int argc, char** argv) {
grpc::testing::InitBenchmark(&argc, &argv, true);
bool ok = grpc::testing::QpsDriver();
diff --git a/test/cpp/qps/qps_openloop_test.cc b/test/cpp/qps/qps_openloop_test.cc
index 8dc50ac6d8..70e2709ac0 100644
--- a/test/cpp/qps/qps_openloop_test.cc
+++ b/test/cpp/qps/qps_openloop_test.cc
@@ -56,7 +56,7 @@ static void RunQPS() {
client_config.set_async_client_threads(8);
client_config.set_rpc_type(STREAMING);
client_config.mutable_load_params()->mutable_poisson()->set_offered_load(
- 1000.0 / GRPC_TEST_SLOWDOWN_FACTOR);
+ 1000.0 / grpc_test_slowdown_factor());
ServerConfig server_config;
server_config.set_server_type(ASYNC_SERVER);
diff --git a/test/cpp/qps/qps_test_with_poll.cc b/test/cpp/qps/qps_test_with_poll.cc
deleted file mode 100644
index c64e6c9d49..0000000000
--- a/test/cpp/qps/qps_test_with_poll.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- *
- * Copyright 2015, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include <set>
-
-#include <grpc/support/log.h>
-
-#include "test/cpp/qps/driver.h"
-#include "test/cpp/qps/report.h"
-#include "test/cpp/util/benchmark_config.h"
-
-extern "C" {
-#include "src/core/lib/iomgr/pollset_posix.h"
-}
-
-namespace grpc {
-namespace testing {
-
-static const int WARMUP = 5;
-static const int BENCHMARK = 5;
-
-static void RunQPS() {
- gpr_log(GPR_INFO, "Running QPS test");
-
- ClientConfig client_config;
- client_config.set_client_type(ASYNC_CLIENT);
- client_config.set_outstanding_rpcs_per_channel(1000);
- client_config.set_client_channels(8);
- client_config.set_async_client_threads(8);
- client_config.set_rpc_type(UNARY);
- client_config.mutable_load_params()->mutable_closed_loop();
-
- ServerConfig server_config;
- server_config.set_server_type(ASYNC_SERVER);
- server_config.set_async_server_threads(4);
-
- const auto result =
- RunScenario(client_config, 1, server_config, 1, WARMUP, BENCHMARK, -2);
-
- GetReporter()->ReportQPSPerCore(*result);
- GetReporter()->ReportLatency(*result);
-}
-
-} // namespace testing
-} // namespace grpc
-
-int main(int argc, char** argv) {
- grpc::testing::InitBenchmark(&argc, &argv, true);
-
- grpc_platform_become_multipoller = grpc_poll_become_multipoller;
-
- grpc::testing::RunQPS();
-
- return 0;
-}
diff --git a/test/cpp/qps/qps_worker.cc b/test/cpp/qps/qps_worker.cc
index d3e53fe14a..d437920e68 100644
--- a/test/cpp/qps/qps_worker.cc
+++ b/test/cpp/qps/qps_worker.cc
@@ -100,7 +100,7 @@ static std::unique_ptr<Server> CreateServer(const ServerConfig& config) {
abort();
}
-class ScopedProfile GRPC_FINAL {
+class ScopedProfile final {
public:
ScopedProfile(const char* filename, bool enable) : enable_(enable) {
if (enable_) grpc_profiler_start(filename);
@@ -113,14 +113,14 @@ class ScopedProfile GRPC_FINAL {
const bool enable_;
};
-class WorkerServiceImpl GRPC_FINAL : public WorkerService::Service {
+class WorkerServiceImpl final : public WorkerService::Service {
public:
WorkerServiceImpl(int server_port, QpsWorker* worker)
: acquired_(false), server_port_(server_port), worker_(worker) {}
- Status RunClient(ServerContext* ctx,
- ServerReaderWriter<ClientStatus, ClientArgs>* stream)
- GRPC_OVERRIDE {
+ Status RunClient(
+ ServerContext* ctx,
+ ServerReaderWriter<ClientStatus, ClientArgs>* stream) override {
InstanceGuard g(this);
if (!g.Acquired()) {
return Status(StatusCode::RESOURCE_EXHAUSTED, "Client worker busy");
@@ -132,9 +132,9 @@ class WorkerServiceImpl GRPC_FINAL : public WorkerService::Service {
return ret;
}
- Status RunServer(ServerContext* ctx,
- ServerReaderWriter<ServerStatus, ServerArgs>* stream)
- GRPC_OVERRIDE {
+ Status RunServer(
+ ServerContext* ctx,
+ ServerReaderWriter<ServerStatus, ServerArgs>* stream) override {
InstanceGuard g(this);
if (!g.Acquired()) {
return Status(StatusCode::RESOURCE_EXHAUSTED, "Server worker busy");
@@ -147,12 +147,12 @@ class WorkerServiceImpl GRPC_FINAL : public WorkerService::Service {
}
Status CoreCount(ServerContext* ctx, const CoreRequest*,
- CoreResponse* resp) GRPC_OVERRIDE {
+ CoreResponse* resp) override {
resp->set_cores(gpr_cpu_num_cores());
return Status::OK;
}
- Status QuitWorker(ServerContext* ctx, const Void*, Void*) GRPC_OVERRIDE {
+ Status QuitWorker(ServerContext* ctx, const Void*, Void*) override {
InstanceGuard g(this);
if (!g.Acquired()) {
return Status(StatusCode::RESOURCE_EXHAUSTED, "Quitting worker busy");
diff --git a/test/cpp/qps/report.cc b/test/cpp/qps/report.cc
index 2ec7d8676c..7f84816421 100644
--- a/test/cpp/qps/report.cc
+++ b/test/cpp/qps/report.cc
@@ -71,8 +71,20 @@ void CompositeReporter::ReportTimes(const ScenarioResult& result) {
}
}
+void CompositeReporter::ReportCpuUsage(const ScenarioResult& result) {
+ for (size_t i = 0; i < reporters_.size(); ++i) {
+ reporters_[i]->ReportCpuUsage(result);
+ }
+}
+
void GprLogReporter::ReportQPS(const ScenarioResult& result) {
gpr_log(GPR_INFO, "QPS: %.1f", result.summary().qps());
+ if (result.summary().failed_requests_per_second() > 0) {
+ gpr_log(GPR_INFO, "failed requests/second: %.1f",
+ result.summary().failed_requests_per_second());
+ gpr_log(GPR_INFO, "successful requests/second: %.1f",
+ result.summary().successful_requests_per_second());
+ }
}
void GprLogReporter::ReportQPSPerCore(const ScenarioResult& result) {
@@ -101,6 +113,11 @@ void GprLogReporter::ReportTimes(const ScenarioResult& result) {
result.summary().client_user_time());
}
+void GprLogReporter::ReportCpuUsage(const ScenarioResult& result) {
+ gpr_log(GPR_INFO, "Server CPU usage: %.2f%%",
+ result.summary().server_cpu_usage());
+}
+
void JsonReporter::ReportQPS(const ScenarioResult& result) {
grpc::string json_string =
SerializeJson(result, "type.googleapis.com/grpc.testing.ScenarioResult");
@@ -121,5 +138,9 @@ void JsonReporter::ReportTimes(const ScenarioResult& result) {
// NOP - all reporting is handled by ReportQPS.
}
+void JsonReporter::ReportCpuUsage(const ScenarioResult& result) {
+ // NOP - all reporting is handled by ReportQPS.
+}
+
} // namespace testing
} // namespace grpc
diff --git a/test/cpp/qps/report.h b/test/cpp/qps/report.h
index 39cf498e7b..faf87ff060 100644
--- a/test/cpp/qps/report.h
+++ b/test/cpp/qps/report.h
@@ -70,6 +70,9 @@ class Reporter {
/** Reports system and user time for client and server systems. */
virtual void ReportTimes(const ScenarioResult& result) = 0;
+ /** Reports server cpu usage. */
+ virtual void ReportCpuUsage(const ScenarioResult& result) = 0;
+
private:
const string name_;
};
@@ -82,10 +85,11 @@ class CompositeReporter : public Reporter {
/** Adds a \a reporter to the composite. */
void add(std::unique_ptr<Reporter> reporter);
- void ReportQPS(const ScenarioResult& result) GRPC_OVERRIDE;
- void ReportQPSPerCore(const ScenarioResult& result) GRPC_OVERRIDE;
- void ReportLatency(const ScenarioResult& result) GRPC_OVERRIDE;
- void ReportTimes(const ScenarioResult& result) GRPC_OVERRIDE;
+ void ReportQPS(const ScenarioResult& result) override;
+ void ReportQPSPerCore(const ScenarioResult& result) override;
+ void ReportLatency(const ScenarioResult& result) override;
+ void ReportTimes(const ScenarioResult& result) override;
+ void ReportCpuUsage(const ScenarioResult& result) override;
private:
std::vector<std::unique_ptr<Reporter> > reporters_;
@@ -97,10 +101,11 @@ class GprLogReporter : public Reporter {
GprLogReporter(const string& name) : Reporter(name) {}
private:
- void ReportQPS(const ScenarioResult& result) GRPC_OVERRIDE;
- void ReportQPSPerCore(const ScenarioResult& result) GRPC_OVERRIDE;
- void ReportLatency(const ScenarioResult& result) GRPC_OVERRIDE;
- void ReportTimes(const ScenarioResult& result) GRPC_OVERRIDE;
+ void ReportQPS(const ScenarioResult& result) override;
+ void ReportQPSPerCore(const ScenarioResult& result) override;
+ void ReportLatency(const ScenarioResult& result) override;
+ void ReportTimes(const ScenarioResult& result) override;
+ void ReportCpuUsage(const ScenarioResult& result) override;
};
/** Dumps the report to a JSON file. */
@@ -110,10 +115,11 @@ class JsonReporter : public Reporter {
: Reporter(name), report_file_(report_file) {}
private:
- void ReportQPS(const ScenarioResult& result) GRPC_OVERRIDE;
- void ReportQPSPerCore(const ScenarioResult& result) GRPC_OVERRIDE;
- void ReportLatency(const ScenarioResult& result) GRPC_OVERRIDE;
- void ReportTimes(const ScenarioResult& result) GRPC_OVERRIDE;
+ void ReportQPS(const ScenarioResult& result) override;
+ void ReportQPSPerCore(const ScenarioResult& result) override;
+ void ReportLatency(const ScenarioResult& result) override;
+ void ReportTimes(const ScenarioResult& result) override;
+ void ReportCpuUsage(const ScenarioResult& result) override;
const string report_file_;
};
diff --git a/test/cpp/qps/server.h b/test/cpp/qps/server.h
index e8bc396696..821d5935be 100644
--- a/test/cpp/qps/server.h
+++ b/test/cpp/qps/server.h
@@ -42,7 +42,6 @@
#include "src/proto/grpc/testing/messages.grpc.pb.h"
#include "test/core/end2end/data/ssl_test_data.h"
#include "test/core/util/port.h"
-#include "test/cpp/qps/limit_cores.h"
#include "test/cpp/qps/usage_timer.h"
namespace grpc {
@@ -51,7 +50,7 @@ namespace testing {
class Server {
public:
explicit Server(const ServerConfig& config) : timer_(new UsageTimer) {
- cores_ = LimitCores(config.core_list().data(), config.core_list_size());
+ cores_ = gpr_cpu_num_cores();
if (config.port()) {
port_ = config.port();
@@ -75,6 +74,8 @@ class Server {
stats.set_time_elapsed(timer_result.wall);
stats.set_time_system(timer_result.system);
stats.set_time_user(timer_result.user);
+ stats.set_total_cpu_time(timer_result.total_cpu_time);
+ stats.set_idle_cpu_time(timer_result.idle_cpu_time);
return stats;
}
diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc
index 082b4bc72f..b3a06aeaf5 100644
--- a/test/cpp/qps/server_async.cc
+++ b/test/cpp/qps/server_async.cc
@@ -38,6 +38,7 @@
#include <thread>
#include <grpc++/generic/async_generic_service.h>
+#include <grpc++/resource_quota.h>
#include <grpc++/security/server_credentials.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
@@ -57,7 +58,7 @@ namespace testing {
template <class RequestType, class ResponseType, class ServiceType,
class ServerContextType>
-class AsyncQpsServerTest : public Server {
+class AsyncQpsServerTest final : public grpc::testing::Server {
public:
AsyncQpsServerTest(
const ServerConfig &config,
@@ -95,6 +96,11 @@ class AsyncQpsServerTest : public Server {
srv_cqs_.emplace_back(builder.AddCompletionQueue());
}
+ if (config.resource_quota_size() > 0) {
+ builder.SetResourceQuota(ResourceQuota("AsyncQpsServerTest")
+ .Resize(config.resource_quota_size()));
+ }
+
server_ = builder.BuildAndStart();
using namespace std::placeholders;
@@ -131,9 +137,7 @@ class AsyncQpsServerTest : public Server {
std::lock_guard<std::mutex> lock((*ss)->mutex);
(*ss)->shutdown = true;
}
- // TODO (vpai): Remove this deadline and allow Shutdown to finish properly
- auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(3);
- server_->Shutdown(deadline);
+ std::thread shutdown_thread(&AsyncQpsServerTest::ShutdownThreadFunc, this);
for (auto cq = srv_cqs_.begin(); cq != srv_cqs_.end(); ++cq) {
(*cq)->Shutdown();
}
@@ -146,9 +150,16 @@ class AsyncQpsServerTest : public Server {
while ((*cq)->Next(&got_tag, &ok))
;
}
+ shutdown_thread.join();
}
private:
+ void ShutdownThreadFunc() {
+ // TODO (vpai): Remove this deadline and allow Shutdown to finish properly
+ auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(3);
+ server_->Shutdown(deadline);
+ }
+
void ThreadFunc(int thread_idx) {
// Wait until work is available or we are shutting down
bool ok;
@@ -185,7 +196,7 @@ class AsyncQpsServerTest : public Server {
return reinterpret_cast<ServerRpcContext *>(tag);
}
- class ServerRpcContextUnaryImpl GRPC_FINAL : public ServerRpcContext {
+ class ServerRpcContextUnaryImpl final : public ServerRpcContext {
public:
ServerRpcContextUnaryImpl(
std::function<void(ServerContextType *, RequestType *,
@@ -202,11 +213,9 @@ class AsyncQpsServerTest : public Server {
request_method_(srv_ctx_.get(), &req_, &response_writer_,
AsyncQpsServerTest::tag(this));
}
- ~ServerRpcContextUnaryImpl() GRPC_OVERRIDE {}
- bool RunNextState(bool ok) GRPC_OVERRIDE {
- return (this->*next_state_)(ok);
- }
- void Reset() GRPC_OVERRIDE {
+ ~ServerRpcContextUnaryImpl() override {}
+ bool RunNextState(bool ok) override { return (this->*next_state_)(ok); }
+ void Reset() override {
srv_ctx_.reset(new ServerContextType);
req_ = RequestType();
response_writer_ =
@@ -246,7 +255,7 @@ class AsyncQpsServerTest : public Server {
grpc::ServerAsyncResponseWriter<ResponseType> response_writer_;
};
- class ServerRpcContextStreamingImpl GRPC_FINAL : public ServerRpcContext {
+ class ServerRpcContextStreamingImpl final : public ServerRpcContext {
public:
ServerRpcContextStreamingImpl(
std::function<void(
@@ -262,11 +271,9 @@ class AsyncQpsServerTest : public Server {
stream_(srv_ctx_.get()) {
request_method_(srv_ctx_.get(), &stream_, AsyncQpsServerTest::tag(this));
}
- ~ServerRpcContextStreamingImpl() GRPC_OVERRIDE {}
- bool RunNextState(bool ok) GRPC_OVERRIDE {
- return (this->*next_state_)(ok);
- }
- void Reset() GRPC_OVERRIDE {
+ ~ServerRpcContextStreamingImpl() override {}
+ bool RunNextState(bool ok) override { return (this->*next_state_)(ok); }
+ void Reset() override {
srv_ctx_.reset(new ServerContextType);
req_ = RequestType();
stream_ = grpc::ServerAsyncReaderWriter<ResponseType, RequestType>(
@@ -369,7 +376,7 @@ static Status ProcessGenericRPC(const PayloadConfig &payload_config,
ByteBuffer *response) {
int resp_size = payload_config.bytebuf_params().resp_size();
std::unique_ptr<char[]> buf(new char[resp_size]);
- gpr_slice s = gpr_slice_from_copied_buffer(buf.get(), resp_size);
+ grpc_slice s = grpc_slice_from_copied_buffer(buf.get(), resp_size);
Slice slice(s, Slice::STEAL_REF);
*response = ByteBuffer(&slice, 1);
return Status::OK;
diff --git a/test/cpp/qps/server_sync.cc b/test/cpp/qps/server_sync.cc
index c774985bfa..8076a4a6b9 100644
--- a/test/cpp/qps/server_sync.cc
+++ b/test/cpp/qps/server_sync.cc
@@ -31,8 +31,7 @@
*
*/
-#include <thread>
-
+#include <grpc++/resource_quota.h>
#include <grpc++/security/server_credentials.h>
#include <grpc++/server.h>
#include <grpc++/server_builder.h>
@@ -49,10 +48,10 @@
namespace grpc {
namespace testing {
-class BenchmarkServiceImpl GRPC_FINAL : public BenchmarkService::Service {
+class BenchmarkServiceImpl final : public BenchmarkService::Service {
public:
Status UnaryCall(ServerContext* context, const SimpleRequest* request,
- SimpleResponse* response) GRPC_OVERRIDE {
+ SimpleResponse* response) override {
if (request->response_size() > 0) {
if (!Server::SetPayload(request->response_type(),
request->response_size(),
@@ -64,7 +63,7 @@ class BenchmarkServiceImpl GRPC_FINAL : public BenchmarkService::Service {
}
Status StreamingCall(
ServerContext* context,
- ServerReaderWriter<SimpleResponse, SimpleRequest>* stream) GRPC_OVERRIDE {
+ ServerReaderWriter<SimpleResponse, SimpleRequest>* stream) override {
SimpleRequest request;
while (stream->Read(&request)) {
SimpleResponse response;
@@ -81,7 +80,7 @@ class BenchmarkServiceImpl GRPC_FINAL : public BenchmarkService::Service {
}
};
-class SynchronousServer GRPC_FINAL : public grpc::testing::Server {
+class SynchronousServer final : public grpc::testing::Server {
public:
explicit SynchronousServer(const ServerConfig& config) : Server(config) {
ServerBuilder builder;
@@ -93,6 +92,11 @@ class SynchronousServer GRPC_FINAL : public grpc::testing::Server {
Server::CreateServerCredentials(config));
gpr_free(server_address);
+ if (config.resource_quota_size() > 0) {
+ builder.SetResourceQuota(ResourceQuota("AsyncQpsServerTest")
+ .Resize(config.resource_quota_size()));
+ }
+
builder.RegisterService(&service_);
impl_ = builder.BuildAndStart();
diff --git a/test/cpp/qps/usage_timer.cc b/test/cpp/qps/usage_timer.cc
index ff595b2ba0..70ef26e82a 100644
--- a/test/cpp/qps/usage_timer.cc
+++ b/test/cpp/qps/usage_timer.cc
@@ -33,10 +33,21 @@
#include "test/cpp/qps/usage_timer.h"
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#include <grpc/support/log.h>
#include <grpc/support/time.h>
+#ifdef __linux__
#include <sys/resource.h>
#include <sys/time.h>
+static double time_double(struct timeval* tv) {
+ return tv->tv_sec + 1e-6 * tv->tv_usec;
+}
+#endif
+
UsageTimer::UsageTimer() : start_(Sample()) {}
double UsageTimer::Now() {
@@ -44,20 +55,46 @@ double UsageTimer::Now() {
return ts.tv_sec + 1e-9 * ts.tv_nsec;
}
-static double time_double(struct timeval* tv) {
- return tv->tv_sec + 1e-6 * tv->tv_usec;
-}
-
-UsageTimer::Result UsageTimer::Sample() {
+static void get_resource_usage(double* utime, double* stime) {
+#ifdef __linux__
struct rusage usage;
- struct timeval tv;
- gettimeofday(&tv, NULL);
getrusage(RUSAGE_SELF, &usage);
+ *utime = time_double(&usage.ru_utime);
+ *stime = time_double(&usage.ru_stime);
+#else
+ *utime = 0;
+ *stime = 0;
+#endif
+}
+static void get_cpu_usage(unsigned long long* total_cpu_time,
+ unsigned long long* idle_cpu_time) {
+#ifdef __linux__
+ std::ifstream proc_stat("/proc/stat");
+ proc_stat.ignore(5);
+ std::string cpu_time_str;
+ std::string first_line;
+ std::getline(proc_stat, first_line);
+ std::stringstream first_line_s(first_line);
+ for (int i = 0; i < 10; ++i) {
+ std::getline(first_line_s, cpu_time_str, ' ');
+ *total_cpu_time += std::stol(cpu_time_str);
+ if (i == 3) {
+ *idle_cpu_time = std::stol(cpu_time_str);
+ }
+ }
+#else
+ gpr_log(GPR_INFO, "get_cpu_usage(): Non-linux platform is not supported.");
+#endif
+}
+
+UsageTimer::Result UsageTimer::Sample() {
Result r;
- r.wall = time_double(&tv);
- r.user = time_double(&usage.ru_utime);
- r.system = time_double(&usage.ru_stime);
+ r.wall = Now();
+ get_resource_usage(&r.user, &r.system);
+ r.total_cpu_time = 0;
+ r.idle_cpu_time = 0;
+ get_cpu_usage(&r.total_cpu_time, &r.idle_cpu_time);
return r;
}
@@ -67,5 +104,8 @@ UsageTimer::Result UsageTimer::Mark() const {
r.wall = s.wall - start_.wall;
r.user = s.user - start_.user;
r.system = s.system - start_.system;
+ r.total_cpu_time = s.total_cpu_time - start_.total_cpu_time;
+ r.idle_cpu_time = s.idle_cpu_time - start_.idle_cpu_time;
+
return r;
}
diff --git a/test/cpp/qps/usage_timer.h b/test/cpp/qps/usage_timer.h
index 8343cd6653..0fc1b47996 100644
--- a/test/cpp/qps/usage_timer.h
+++ b/test/cpp/qps/usage_timer.h
@@ -42,6 +42,8 @@ class UsageTimer {
double wall;
double user;
double system;
+ unsigned long long total_cpu_time;
+ unsigned long long idle_cpu_time;
};
Result Mark() const;
diff --git a/test/cpp/qps/worker.cc b/test/cpp/qps/worker.cc
index 2068b7c213..e88d0647dd 100644
--- a/test/cpp/qps/worker.cc
+++ b/test/cpp/qps/worker.cc
@@ -31,7 +31,7 @@
*
*/
-#include <sys/signal.h>
+#include <signal.h>
#include <chrono>
#include <thread>
diff --git a/test/cpp/test/server_context_test_spouse_test.cc b/test/cpp/test/server_context_test_spouse_test.cc
new file mode 100644
index 0000000000..eb279e2401
--- /dev/null
+++ b/test/cpp/test/server_context_test_spouse_test.cc
@@ -0,0 +1,111 @@
+/*
+ *
+ * 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 <grpc++/test/server_context_test_spouse.h>
+
+#include <cstring>
+#include <vector>
+
+#include <grpc++/impl/grpc_library.h>
+#include <gtest/gtest.h>
+
+namespace grpc {
+namespace testing {
+
+static internal::GrpcLibraryInitializer g_initializer;
+
+const char key1[] = "metadata-key1";
+const char key2[] = "metadata-key2";
+const char val1[] = "metadata-val1";
+const char val2[] = "metadata-val2";
+
+bool ClientMetadataContains(const ServerContext& context,
+ const grpc::string_ref& key,
+ const grpc::string_ref& value) {
+ const auto& client_metadata = context.client_metadata();
+ for (auto iter = client_metadata.begin(); iter != client_metadata.end();
+ ++iter) {
+ if (iter->first == key && iter->second == value) {
+ return true;
+ }
+ }
+ return false;
+}
+
+TEST(ServerContextTestSpouseTest, ClientMetadata) {
+ ServerContext context;
+ ServerContextTestSpouse spouse(&context);
+
+ spouse.AddClientMetadata(key1, val1);
+ ASSERT_TRUE(ClientMetadataContains(context, key1, val1));
+
+ spouse.AddClientMetadata(key2, val2);
+ ASSERT_TRUE(ClientMetadataContains(context, key1, val1));
+ ASSERT_TRUE(ClientMetadataContains(context, key2, val2));
+}
+
+TEST(ServerContextTestSpouseTest, InitialMetadata) {
+ ServerContext context;
+ ServerContextTestSpouse spouse(&context);
+ std::multimap<grpc::string, grpc::string> metadata;
+
+ context.AddInitialMetadata(key1, val1);
+ metadata.insert(std::pair<grpc::string, grpc::string>(key1, val1));
+ ASSERT_EQ(metadata, spouse.GetInitialMetadata());
+
+ context.AddInitialMetadata(key2, val2);
+ metadata.insert(std::pair<grpc::string, grpc::string>(key2, val2));
+ ASSERT_EQ(metadata, spouse.GetInitialMetadata());
+}
+
+TEST(ServerContextTestSpouseTest, TrailingMetadata) {
+ ServerContext context;
+ ServerContextTestSpouse spouse(&context);
+ std::multimap<grpc::string, grpc::string> metadata;
+
+ context.AddTrailingMetadata(key1, val1);
+ metadata.insert(std::pair<grpc::string, grpc::string>(key1, val1));
+ ASSERT_EQ(metadata, spouse.GetTrailingMetadata());
+
+ context.AddTrailingMetadata(key2, val2);
+ metadata.insert(std::pair<grpc::string, grpc::string>(key2, val2));
+ ASSERT_EQ(metadata, spouse.GetTrailingMetadata());
+}
+
+} // namespace
+} // namespace grpc
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/test/cpp/thread_manager/thread_manager_test.cc b/test/cpp/thread_manager/thread_manager_test.cc
new file mode 100644
index 0000000000..35c8d5d088
--- /dev/null
+++ b/test/cpp/thread_manager/thread_manager_test.cc
@@ -0,0 +1,136 @@
+/*
+ *
+ * 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.
+ *is % allowed in string
+ */
+
+#include <ctime>
+#include <memory>
+#include <string>
+
+#include <gflags/gflags.h>
+#include <grpc++/grpc++.h>
+#include <grpc/support/log.h>
+#include <grpc/support/port_platform.h>
+
+#include "src/cpp/thread_manager/thread_manager.h"
+#include "test/cpp/util/test_config.h"
+
+namespace grpc {
+class ThreadManagerTest final : public grpc::ThreadManager {
+ public:
+ ThreadManagerTest()
+ : ThreadManager(kMinPollers, kMaxPollers),
+ num_do_work_(0),
+ num_poll_for_work_(0),
+ num_work_found_(0) {}
+
+ grpc::ThreadManager::WorkStatus PollForWork(void **tag, bool *ok) override;
+ void DoWork(void *tag, bool ok) override;
+ void PerformTest();
+
+ private:
+ void SleepForMs(int sleep_time_ms);
+
+ static const int kMinPollers = 2;
+ static const int kMaxPollers = 10;
+
+ static const int kPollingTimeoutMsec = 10;
+ static const int kDoWorkDurationMsec = 1;
+
+ // PollForWork will return SHUTDOWN after these many number of invocations
+ static const int kMaxNumPollForWork = 50;
+
+ gpr_atm num_do_work_; // Number of calls to DoWork
+ gpr_atm num_poll_for_work_; // Number of calls to PollForWork
+ gpr_atm num_work_found_; // Number of times WORK_FOUND was returned
+};
+
+void ThreadManagerTest::SleepForMs(int duration_ms) {
+ gpr_timespec sleep_time =
+ gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
+ gpr_time_from_millis(duration_ms, GPR_TIMESPAN));
+ gpr_sleep_until(sleep_time);
+}
+
+grpc::ThreadManager::WorkStatus ThreadManagerTest::PollForWork(void **tag,
+ bool *ok) {
+ int call_num = gpr_atm_no_barrier_fetch_add(&num_poll_for_work_, 1);
+
+ if (call_num >= kMaxNumPollForWork) {
+ Shutdown();
+ return SHUTDOWN;
+ }
+
+ // Simulate "polling for work" by sleeping for sometime
+ SleepForMs(kPollingTimeoutMsec);
+
+ *tag = nullptr;
+ *ok = true;
+
+ // Return timeout roughly 1 out of every 3 calls
+ if (call_num % 3 == 0) {
+ return TIMEOUT;
+ } else {
+ gpr_atm_no_barrier_fetch_add(&num_work_found_, 1);
+ return WORK_FOUND;
+ }
+}
+
+void ThreadManagerTest::DoWork(void *tag, bool ok) {
+ gpr_atm_no_barrier_fetch_add(&num_do_work_, 1);
+ SleepForMs(kDoWorkDurationMsec); // Simulate doing work by sleeping
+}
+
+void ThreadManagerTest::PerformTest() {
+ // Initialize() starts the ThreadManager
+ Initialize();
+
+ // Wait for all the threads to gracefully terminate
+ Wait();
+
+ // The number of times DoWork() was called is equal to the number of times
+ // WORK_FOUND was returned
+ gpr_log(GPR_DEBUG, "DoWork() called %ld times",
+ gpr_atm_no_barrier_load(&num_do_work_));
+ GPR_ASSERT(gpr_atm_no_barrier_load(&num_do_work_) ==
+ gpr_atm_no_barrier_load(&num_work_found_));
+}
+} // namespace grpc
+
+int main(int argc, char **argv) {
+ std::srand(std::time(NULL));
+
+ grpc::testing::InitTest(&argc, &argv, true);
+ grpc::ThreadManagerTest test_rpc_manager;
+ test_rpc_manager.PerformTest();
+
+ return 0;
+}
diff --git a/test/cpp/util/BUILD b/test/cpp/util/BUILD
new file mode 100644
index 0000000000..dc90a4e172
--- /dev/null
+++ b/test/cpp/util/BUILD
@@ -0,0 +1,85 @@
+# Copyright 2017, 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.
+
+licenses(["notice"]) # 3-clause BSD
+
+cc_library(
+ name = "test_config",
+ srcs = [
+ "test_config_cc.cc",
+ ],
+ hdrs = [
+ "test_config.h",
+ ],
+ visibility = ["//test:__subpackages__"],
+ deps = [
+ "//:gpr",
+ "//external:gflags",
+ ],
+)
+
+cc_library(
+ name = "grpc++_proto_reflection_desc_db",
+ srcs = [
+ "proto_reflection_descriptor_database.cc",
+ ],
+ hdrs = [
+ "proto_reflection_descriptor_database.h",
+ ],
+ visibility = ["//test:__subpackages__"],
+ deps = [
+ "//:grpc++_config_proto",
+ "//src/proto/grpc/reflection/v1alpha:reflection_proto",
+ ],
+)
+
+cc_library(
+ name = "test_util",
+ srcs = [
+ # "test/cpp/end2end/test_service_impl.cc",
+ "byte_buffer_proto_helper.cc",
+ "create_test_channel.cc",
+ "string_ref_helper.cc",
+ "subprocess.cc",
+ "test_credentials_provider.cc",
+ ],
+ hdrs = [
+ "byte_buffer_proto_helper.h",
+ "create_test_channel.h",
+ "string_ref_helper.h",
+ "subprocess.h",
+ "test_credentials_provider.h",
+ ],
+ visibility = ["//test:__subpackages__"],
+ deps = [
+ "//:grpc++",
+ "//test/core/end2end:ssl_test_data",
+ "//test/core/util:gpr_test_util",
+ ],
+)
diff --git a/test/cpp/util/byte_buffer_proto_helper.cc b/test/cpp/util/byte_buffer_proto_helper.cc
index d625d6f3f4..ab5ca261e6 100644
--- a/test/cpp/util/byte_buffer_proto_helper.cc
+++ b/test/cpp/util/byte_buffer_proto_helper.cc
@@ -51,7 +51,7 @@ std::unique_ptr<ByteBuffer> SerializeToByteBuffer(
grpc::protobuf::Message* message) {
grpc::string buf;
message->SerializeToString(&buf);
- gpr_slice s = gpr_slice_from_copied_string(buf.c_str());
+ grpc_slice s = grpc_slice_from_copied_string(buf.c_str());
Slice slice(s, Slice::STEAL_REF);
return std::unique_ptr<ByteBuffer>(new ByteBuffer(&slice, 1));
}
diff --git a/test/cpp/util/byte_buffer_test.cc b/test/cpp/util/byte_buffer_test.cc
index 2089a62011..470ce0cbf4 100644
--- a/test/cpp/util/byte_buffer_test.cc
+++ b/test/cpp/util/byte_buffer_test.cc
@@ -37,7 +37,7 @@
#include <vector>
#include <grpc++/support/slice.h>
-#include <grpc/support/slice.h>
+#include <grpc/slice.h>
#include <gtest/gtest.h>
namespace grpc {
@@ -49,14 +49,14 @@ const char* kContent2 = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy world";
class ByteBufferTest : public ::testing::Test {};
TEST_F(ByteBufferTest, CreateFromSingleSlice) {
- gpr_slice hello = gpr_slice_from_copied_string(kContent1);
+ grpc_slice hello = grpc_slice_from_copied_string(kContent1);
Slice s(hello, Slice::STEAL_REF);
ByteBuffer buffer(&s, 1);
}
TEST_F(ByteBufferTest, CreateFromVector) {
- gpr_slice hello = gpr_slice_from_copied_string(kContent1);
- gpr_slice world = gpr_slice_from_copied_string(kContent2);
+ grpc_slice hello = grpc_slice_from_copied_string(kContent1);
+ grpc_slice world = grpc_slice_from_copied_string(kContent2);
std::vector<Slice> slices;
slices.push_back(Slice(hello, Slice::STEAL_REF));
slices.push_back(Slice(world, Slice::STEAL_REF));
@@ -64,15 +64,15 @@ TEST_F(ByteBufferTest, CreateFromVector) {
}
TEST_F(ByteBufferTest, Clear) {
- gpr_slice hello = gpr_slice_from_copied_string(kContent1);
+ grpc_slice hello = grpc_slice_from_copied_string(kContent1);
Slice s(hello, Slice::STEAL_REF);
ByteBuffer buffer(&s, 1);
buffer.Clear();
}
TEST_F(ByteBufferTest, Length) {
- gpr_slice hello = gpr_slice_from_copied_string(kContent1);
- gpr_slice world = gpr_slice_from_copied_string(kContent2);
+ grpc_slice hello = grpc_slice_from_copied_string(kContent1);
+ grpc_slice world = grpc_slice_from_copied_string(kContent2);
std::vector<Slice> slices;
slices.push_back(Slice(hello, Slice::STEAL_REF));
slices.push_back(Slice(world, Slice::STEAL_REF));
@@ -80,12 +80,12 @@ TEST_F(ByteBufferTest, Length) {
EXPECT_EQ(strlen(kContent1) + strlen(kContent2), buffer.Length());
}
-bool SliceEqual(const Slice& a, gpr_slice b) {
- if (a.size() != GPR_SLICE_LENGTH(b)) {
+bool SliceEqual(const Slice& a, grpc_slice b) {
+ if (a.size() != GRPC_SLICE_LENGTH(b)) {
return false;
}
for (size_t i = 0; i < a.size(); i++) {
- if (a.begin()[i] != GPR_SLICE_START_PTR(b)[i]) {
+ if (a.begin()[i] != GRPC_SLICE_START_PTR(b)[i]) {
return false;
}
}
@@ -93,8 +93,8 @@ bool SliceEqual(const Slice& a, gpr_slice b) {
}
TEST_F(ByteBufferTest, Dump) {
- gpr_slice hello = gpr_slice_from_copied_string(kContent1);
- gpr_slice world = gpr_slice_from_copied_string(kContent2);
+ grpc_slice hello = grpc_slice_from_copied_string(kContent1);
+ grpc_slice world = grpc_slice_from_copied_string(kContent2);
std::vector<Slice> slices;
slices.push_back(Slice(hello, Slice::STEAL_REF));
slices.push_back(Slice(world, Slice::STEAL_REF));
@@ -106,8 +106,8 @@ TEST_F(ByteBufferTest, Dump) {
}
TEST_F(ByteBufferTest, SerializationMakesCopy) {
- gpr_slice hello = gpr_slice_from_copied_string(kContent1);
- gpr_slice world = gpr_slice_from_copied_string(kContent2);
+ grpc_slice hello = grpc_slice_from_copied_string(kContent1);
+ grpc_slice world = grpc_slice_from_copied_string(kContent2);
std::vector<Slice> slices;
slices.push_back(Slice(hello, Slice::STEAL_REF));
slices.push_back(Slice(world, Slice::STEAL_REF));
diff --git a/test/cpp/util/cli_call.cc b/test/cpp/util/cli_call.cc
index 1edffbe08e..041cc0e4c3 100644
--- a/test/cpp/util/cli_call.cc
+++ b/test/cpp/util/cli_call.cc
@@ -37,12 +37,10 @@
#include <grpc++/channel.h>
#include <grpc++/client_context.h>
-#include <grpc++/completion_queue.h>
-#include <grpc++/generic/generic_stub.h>
#include <grpc++/support/byte_buffer.h>
#include <grpc/grpc.h>
+#include <grpc/slice.h>
#include <grpc/support/log.h>
-#include <grpc/support/slice.h>
namespace grpc {
namespace testing {
@@ -56,55 +54,172 @@ Status CliCall::Call(std::shared_ptr<grpc::Channel> channel,
const OutgoingMetadataContainer& metadata,
IncomingMetadataContainer* server_initial_metadata,
IncomingMetadataContainer* server_trailing_metadata) {
- std::unique_ptr<grpc::GenericStub> stub(new grpc::GenericStub(channel));
- grpc::ClientContext ctx;
+ CliCall call(channel, method, metadata);
+ call.Write(request);
+ call.WritesDone();
+ if (!call.Read(response, server_initial_metadata)) {
+ fprintf(stderr, "Failed to read response.\n");
+ }
+ return call.Finish(server_trailing_metadata);
+}
+
+CliCall::CliCall(std::shared_ptr<grpc::Channel> channel,
+ const grpc::string& method,
+ const OutgoingMetadataContainer& metadata)
+ : stub_(new grpc::GenericStub(channel)) {
+ gpr_mu_init(&write_mu_);
+ gpr_cv_init(&write_cv_);
if (!metadata.empty()) {
for (OutgoingMetadataContainer::const_iterator iter = metadata.begin();
iter != metadata.end(); ++iter) {
- ctx.AddMetadata(iter->first, iter->second);
+ ctx_.AddMetadata(iter->first, iter->second);
}
}
- grpc::CompletionQueue cq;
- std::unique_ptr<grpc::GenericClientAsyncReaderWriter> call(
- stub->Call(&ctx, method, &cq, tag(1)));
+ call_ = stub_->Call(&ctx_, method, &cq_, tag(1));
void* got_tag;
bool ok;
- cq.Next(&got_tag, &ok);
+ cq_.Next(&got_tag, &ok);
GPR_ASSERT(ok);
+}
+
+CliCall::~CliCall() {
+ gpr_cv_destroy(&write_cv_);
+ gpr_mu_destroy(&write_mu_);
+}
+
+void CliCall::Write(const grpc::string& request) {
+ void* got_tag;
+ bool ok;
- gpr_slice s = gpr_slice_from_copied_string(request.c_str());
+ grpc_slice s = grpc_slice_from_copied_string(request.c_str());
grpc::Slice req_slice(s, grpc::Slice::STEAL_REF);
grpc::ByteBuffer send_buffer(&req_slice, 1);
- call->Write(send_buffer, tag(2));
- cq.Next(&got_tag, &ok);
- GPR_ASSERT(ok);
- call->WritesDone(tag(3));
- cq.Next(&got_tag, &ok);
+ call_->Write(send_buffer, tag(2));
+ cq_.Next(&got_tag, &ok);
GPR_ASSERT(ok);
+}
+
+bool CliCall::Read(grpc::string* response,
+ IncomingMetadataContainer* server_initial_metadata) {
+ void* got_tag;
+ bool ok;
+
grpc::ByteBuffer recv_buffer;
- call->Read(&recv_buffer, tag(4));
- cq.Next(&got_tag, &ok);
- if (!ok) {
- std::cout << "Failed to read response." << std::endl;
+ call_->Read(&recv_buffer, tag(3));
+
+ if (!cq_.Next(&got_tag, &ok) || !ok) {
+ return false;
}
- grpc::Status status;
- call->Finish(&status, tag(5));
- cq.Next(&got_tag, &ok);
+ std::vector<grpc::Slice> slices;
+ GPR_ASSERT(recv_buffer.Dump(&slices).ok());
+
+ response->clear();
+ for (size_t i = 0; i < slices.size(); i++) {
+ response->append(reinterpret_cast<const char*>(slices[i].begin()),
+ slices[i].size());
+ }
+ if (server_initial_metadata) {
+ *server_initial_metadata = ctx_.GetServerInitialMetadata();
+ }
+ return true;
+}
+
+void CliCall::WritesDone() {
+ void* got_tag;
+ bool ok;
+
+ call_->WritesDone(tag(4));
+ cq_.Next(&got_tag, &ok);
GPR_ASSERT(ok);
+}
+
+void CliCall::WriteAndWait(const grpc::string& request) {
+ grpc_slice s = grpc_slice_from_copied_string(request.c_str());
+ grpc::Slice req_slice(s, grpc::Slice::STEAL_REF);
+ grpc::ByteBuffer send_buffer(&req_slice, 1);
+
+ gpr_mu_lock(&write_mu_);
+ call_->Write(send_buffer, tag(2));
+ write_done_ = false;
+ while (!write_done_) {
+ gpr_cv_wait(&write_cv_, &write_mu_, gpr_inf_future(GPR_CLOCK_REALTIME));
+ }
+ gpr_mu_unlock(&write_mu_);
+}
+
+void CliCall::WritesDoneAndWait() {
+ gpr_mu_lock(&write_mu_);
+ call_->WritesDone(tag(4));
+ write_done_ = false;
+ while (!write_done_) {
+ gpr_cv_wait(&write_cv_, &write_mu_, gpr_inf_future(GPR_CLOCK_REALTIME));
+ }
+ gpr_mu_unlock(&write_mu_);
+}
- if (status.ok()) {
- std::vector<grpc::Slice> slices;
- (void)recv_buffer.Dump(&slices);
+bool CliCall::ReadAndMaybeNotifyWrite(
+ grpc::string* response,
+ IncomingMetadataContainer* server_initial_metadata) {
+ void* got_tag;
+ bool ok;
+ grpc::ByteBuffer recv_buffer;
- response->clear();
- for (size_t i = 0; i < slices.size(); i++) {
- response->append(reinterpret_cast<const char*>(slices[i].begin()),
- slices[i].size());
+ call_->Read(&recv_buffer, tag(3));
+ bool cq_result = cq_.Next(&got_tag, &ok);
+
+ while (got_tag != tag(3)) {
+ gpr_mu_lock(&write_mu_);
+ write_done_ = true;
+ gpr_cv_signal(&write_cv_);
+ gpr_mu_unlock(&write_mu_);
+
+ cq_result = cq_.Next(&got_tag, &ok);
+ if (got_tag == tag(2)) {
+ GPR_ASSERT(ok);
}
}
- *server_initial_metadata = ctx.GetServerInitialMetadata();
- *server_trailing_metadata = ctx.GetServerTrailingMetadata();
+ if (!cq_result || !ok) {
+ // If the RPC is ended on the server side, we should still wait for the
+ // pending write on the client side to be done.
+ if (!ok) {
+ gpr_mu_lock(&write_mu_);
+ if (!write_done_) {
+ cq_.Next(&got_tag, &ok);
+ GPR_ASSERT(got_tag != tag(2));
+ write_done_ = true;
+ gpr_cv_signal(&write_cv_);
+ }
+ gpr_mu_unlock(&write_mu_);
+ }
+ return false;
+ }
+
+ std::vector<grpc::Slice> slices;
+ GPR_ASSERT(recv_buffer.Dump(&slices).ok());
+ response->clear();
+ for (size_t i = 0; i < slices.size(); i++) {
+ response->append(reinterpret_cast<const char*>(slices[i].begin()),
+ slices[i].size());
+ }
+ if (server_initial_metadata) {
+ *server_initial_metadata = ctx_.GetServerInitialMetadata();
+ }
+ return true;
+}
+
+Status CliCall::Finish(IncomingMetadataContainer* server_trailing_metadata) {
+ void* got_tag;
+ bool ok;
+ grpc::Status status;
+
+ call_->Finish(&status, tag(5));
+ cq_.Next(&got_tag, &ok);
+ GPR_ASSERT(ok);
+ if (server_trailing_metadata) {
+ *server_trailing_metadata = ctx_.GetServerTrailingMetadata();
+ }
+
return status;
}
diff --git a/test/cpp/util/cli_call.h b/test/cpp/util/cli_call.h
index 2fbc9618b6..91f0dbc9ed 100644
--- a/test/cpp/util/cli_call.h
+++ b/test/cpp/util/cli_call.h
@@ -37,23 +37,74 @@
#include <map>
#include <grpc++/channel.h>
+#include <grpc++/completion_queue.h>
+#include <grpc++/generic/generic_stub.h>
#include <grpc++/support/status.h>
#include <grpc++/support/string_ref.h>
namespace grpc {
+
+class ClientContext;
+
namespace testing {
-class CliCall GRPC_FINAL {
+// CliCall handles the sending and receiving of generic messages given the name
+// of the remote method. This class is only used by GrpcTool. Its thread-safe
+// and thread-unsafe methods should not be used together.
+class CliCall final {
public:
typedef std::multimap<grpc::string, grpc::string> OutgoingMetadataContainer;
typedef std::multimap<grpc::string_ref, grpc::string_ref>
IncomingMetadataContainer;
+
+ CliCall(std::shared_ptr<grpc::Channel> channel, const grpc::string& method,
+ const OutgoingMetadataContainer& metadata);
+ ~CliCall();
+
+ // Perform an unary generic RPC.
static Status Call(std::shared_ptr<grpc::Channel> channel,
const grpc::string& method, const grpc::string& request,
grpc::string* response,
const OutgoingMetadataContainer& metadata,
IncomingMetadataContainer* server_initial_metadata,
IncomingMetadataContainer* server_trailing_metadata);
+
+ // Send a generic request message in a synchronous manner. NOT thread-safe.
+ void Write(const grpc::string& request);
+
+ // Send a generic request message in a synchronous manner. NOT thread-safe.
+ void WritesDone();
+
+ // Receive a generic response message in a synchronous manner.NOT thread-safe.
+ bool Read(grpc::string* response,
+ IncomingMetadataContainer* server_initial_metadata);
+
+ // Thread-safe write. Must be used with ReadAndMaybeNotifyWrite. Send out a
+ // generic request message and wait for ReadAndMaybeNotifyWrite to finish it.
+ void WriteAndWait(const grpc::string& request);
+
+ // Thread-safe WritesDone. Must be used with ReadAndMaybeNotifyWrite. Send out
+ // WritesDone for gereneric request messages and wait for
+ // ReadAndMaybeNotifyWrite to finish it.
+ void WritesDoneAndWait();
+
+ // Thread-safe Read. Blockingly receive a generic response message. Notify
+ // writes if they are finished when this read is waiting for a resposne.
+ bool ReadAndMaybeNotifyWrite(
+ grpc::string* response,
+ IncomingMetadataContainer* server_initial_metadata);
+
+ // Finish the RPC.
+ Status Finish(IncomingMetadataContainer* server_trailing_metadata);
+
+ private:
+ std::unique_ptr<grpc::GenericStub> stub_;
+ grpc::ClientContext ctx_;
+ std::unique_ptr<grpc::GenericClientAsyncReaderWriter> call_;
+ grpc::CompletionQueue cq_;
+ gpr_mu write_mu_;
+ gpr_cv write_cv_; // Protected by write_mu_;
+ bool write_done_; // Portected by write_mu_;
};
} // namespace testing
diff --git a/test/cpp/util/cli_call_test.cc b/test/cpp/util/cli_call_test.cc
index 75e90f824f..2e8501b2c3 100644
--- a/test/cpp/util/cli_call_test.cc
+++ b/test/cpp/util/cli_call_test.cc
@@ -56,7 +56,7 @@ namespace testing {
class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
public:
Status Echo(ServerContext* context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
if (!context->client_metadata().empty()) {
for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator
iter = context->client_metadata().begin();
@@ -75,7 +75,7 @@ class CliCallTest : public ::testing::Test {
protected:
CliCallTest() {}
- void SetUp() GRPC_OVERRIDE {
+ void SetUp() override {
int port = grpc_pick_unused_port_or_die();
server_address_ << "localhost:" << port;
// Setup server
@@ -86,7 +86,7 @@ class CliCallTest : public ::testing::Test {
server_ = builder.BuildAndStart();
}
- void TearDown() GRPC_OVERRIDE { server_->Shutdown(); }
+ void TearDown() override { server_->Shutdown(); }
void ResetStub() {
channel_ =
diff --git a/test/cpp/util/config_grpc_cli.h b/test/cpp/util/config_grpc_cli.h
index ea8231aa26..ac1e3044b7 100644
--- a/test/cpp/util/config_grpc_cli.h
+++ b/test/cpp/util/config_grpc_cli.h
@@ -77,7 +77,7 @@ namespace compiler {
typedef GRPC_CUSTOM_DISKSOURCETREE DiskSourceTree;
typedef GRPC_CUSTOM_IMPORTER Importer;
typedef GRPC_CUSTOM_MULTIFILEERRORCOLLECTOR MultiFileErrorCollector;
-} // namespace importer
+} // namespace compiler
} // namespace protobuf
} // namespace grpc
diff --git a/test/cpp/util/create_test_channel.cc b/test/cpp/util/create_test_channel.cc
index fe8b5d5423..ad62e03490 100644
--- a/test/cpp/util/create_test_channel.cc
+++ b/test/cpp/util/create_test_channel.cc
@@ -35,11 +35,37 @@
#include <grpc++/create_channel.h>
#include <grpc++/security/credentials.h>
+#include <grpc/support/log.h>
-#include "test/core/end2end/data/ssl_test_data.h"
+#include "test/cpp/util/test_credentials_provider.h"
namespace grpc {
+namespace {
+
+const char kProdTlsCredentialsType[] = "prod_ssl";
+
+class SslCredentialProvider : public testing::CredentialTypeProvider {
+ public:
+ std::shared_ptr<ChannelCredentials> GetChannelCredentials(
+ grpc::ChannelArguments* args) override {
+ return SslCredentials(SslCredentialsOptions());
+ }
+ std::shared_ptr<ServerCredentials> GetServerCredentials() override {
+ return nullptr;
+ }
+};
+
+gpr_once g_once_init_add_prod_ssl_provider = GPR_ONCE_INIT;
+// Register ssl with non-test roots type to the credentials provider.
+void AddProdSslType() {
+ testing::GetCredentialsProvider()->AddSecureType(
+ kProdTlsCredentialsType, std::unique_ptr<testing::CredentialTypeProvider>(
+ new SslCredentialProvider));
+}
+
+} // namespace
+
// When ssl is enabled, if server is empty, override_hostname is used to
// create channel. Otherwise, connect to server and override hostname if
// override_hostname is provided.
@@ -61,16 +87,22 @@ std::shared_ptr<Channel> CreateTestChannel(
const std::shared_ptr<CallCredentials>& creds,
const ChannelArguments& args) {
ChannelArguments channel_args(args);
+ std::shared_ptr<ChannelCredentials> channel_creds;
if (enable_ssl) {
- const char* roots_certs = use_prod_roots ? "" : test_root_cert;
- SslCredentialsOptions ssl_opts = {roots_certs, "", ""};
-
- std::shared_ptr<ChannelCredentials> channel_creds =
- SslCredentials(ssl_opts);
-
- if (!server.empty() && !override_hostname.empty()) {
- channel_args.SetSslTargetNameOverride(override_hostname);
+ if (use_prod_roots) {
+ gpr_once_init(&g_once_init_add_prod_ssl_provider, &AddProdSslType);
+ channel_creds = testing::GetCredentialsProvider()->GetChannelCredentials(
+ kProdTlsCredentialsType, &channel_args);
+ if (!server.empty() && !override_hostname.empty()) {
+ channel_args.SetSslTargetNameOverride(override_hostname);
+ }
+ } else {
+ // override_hostname is discarded as the provider handles it.
+ channel_creds = testing::GetCredentialsProvider()->GetChannelCredentials(
+ testing::kTlsCredentialsType, &channel_args);
}
+ GPR_ASSERT(channel_creds != nullptr);
+
const grpc::string& connect_to =
server.empty() ? override_hostname : server;
if (creds.get()) {
@@ -103,4 +135,18 @@ std::shared_ptr<Channel> CreateTestChannel(const grpc::string& server,
return CreateTestChannel(server, "foo.test.google.fr", enable_ssl, false);
}
+std::shared_ptr<Channel> CreateTestChannel(
+ const grpc::string& server, const grpc::string& credential_type,
+ const std::shared_ptr<CallCredentials>& creds) {
+ ChannelArguments channel_args;
+ std::shared_ptr<ChannelCredentials> channel_creds =
+ testing::GetCredentialsProvider()->GetChannelCredentials(credential_type,
+ &channel_args);
+ GPR_ASSERT(channel_creds != nullptr);
+ if (creds.get()) {
+ channel_creds = CompositeChannelCredentials(channel_creds, creds);
+ }
+ return CreateCustomChannel(server, channel_creds, channel_args);
+}
+
} // namespace grpc
diff --git a/test/cpp/util/create_test_channel.h b/test/cpp/util/create_test_channel.h
index 4ff666dc1b..ce71a97edb 100644
--- a/test/cpp/util/create_test_channel.h
+++ b/test/cpp/util/create_test_channel.h
@@ -59,6 +59,10 @@ std::shared_ptr<Channel> CreateTestChannel(
const std::shared_ptr<CallCredentials>& creds,
const ChannelArguments& args);
+std::shared_ptr<Channel> CreateTestChannel(
+ const grpc::string& server, const grpc::string& credential_type,
+ const std::shared_ptr<CallCredentials>& creds);
+
} // namespace grpc
#endif // GRPC_TEST_CPP_UTIL_CREATE_TEST_CHANNEL_H
diff --git a/test/cpp/util/grpc_cli.cc b/test/cpp/util/grpc_cli.cc
index fe248601ee..a78bed4b90 100644
--- a/test/cpp/util/grpc_cli.cc
+++ b/test/cpp/util/grpc_cli.cc
@@ -83,10 +83,10 @@ DEFINE_string(outfile, "", "Output file (default is stdout)");
static bool SimplePrint(const grpc::string& outfile,
const grpc::string& output) {
if (outfile.empty()) {
- std::cout << output;
+ std::cout << output << std::endl;
} else {
- std::ofstream output_file(outfile, std::ios::trunc | std::ios::binary);
- output_file << output;
+ std::ofstream output_file(outfile, std::ios::app | std::ios::binary);
+ output_file << output << std::endl;
output_file.close();
}
return true;
diff --git a/test/cpp/util/grpc_tool.cc b/test/cpp/util/grpc_tool.cc
index 03c33abe9f..856cd32c3c 100644
--- a/test/cpp/util/grpc_tool.cc
+++ b/test/cpp/util/grpc_tool.cc
@@ -33,12 +33,13 @@
#include "test/cpp/util/grpc_tool.h"
-#include <unistd.h>
+#include <cstdio>
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
+#include <thread>
#include <gflags/gflags.h>
#include <grpc++/channel.h>
@@ -47,12 +48,19 @@
#include <grpc++/security/credentials.h>
#include <grpc++/support/string_ref.h>
#include <grpc/grpc.h>
+#include <grpc/support/port_platform.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/service_describer.h"
+#if GPR_WINDOWS
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
namespace grpc {
namespace testing {
@@ -86,11 +94,12 @@ class GrpcTool {
// 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);
+ bool ParseMessage(int argc, const char** argv, const CliCredentials& cred,
+ GrpcToolOutputCallback callback);
+ bool ToText(int argc, const char** argv, const CliCredentials& cred,
+ GrpcToolOutputCallback callback);
+ bool ToBinary(int argc, const char** argv, const CliCredentials& cred,
+ GrpcToolOutputCallback callback);
void SetPrintCommandMode(int exit_status) {
print_command_usage_ = true;
@@ -158,6 +167,36 @@ void PrintMetadata(const T& m, const grpc::string& message) {
}
}
+void ReadResponse(CliCall* call, const grpc::string& method_name,
+ GrpcToolOutputCallback callback, ProtoFileParser* parser,
+ gpr_mu* parser_mu, bool print_mode) {
+ grpc::string serialized_response_proto;
+ std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata;
+
+ for (bool receive_initial_metadata = true; call->ReadAndMaybeNotifyWrite(
+ &serialized_response_proto,
+ receive_initial_metadata ? &server_initial_metadata : nullptr);
+ receive_initial_metadata = false) {
+ fprintf(stderr, "got response.\n");
+ if (!FLAGS_binary_output) {
+ gpr_mu_lock(parser_mu);
+ serialized_response_proto = parser->GetTextFormatFromMethod(
+ method_name, serialized_response_proto, false /* is_request */);
+ if (parser->HasError() && print_mode) {
+ fprintf(stderr, "Failed to parse response.\n");
+ }
+ gpr_mu_unlock(parser_mu);
+ }
+ if (receive_initial_metadata) {
+ PrintMetadata(server_initial_metadata,
+ "Received initial metadata from server:");
+ }
+ if (!callback(serialized_response_proto) && print_mode) {
+ fprintf(stderr, "Failed to output response.\n");
+ }
+ }
+}
+
struct Command {
const char* command;
std::function<bool(GrpcTool*, int, const char**, const CliCredentials&,
@@ -173,9 +212,9 @@ const Command ops[] = {
{"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},
+ {"parse", BindWith5Args(&GrpcTool::ParseMessage), 2, 3},
+ {"totext", BindWith5Args(&GrpcTool::ToText), 2, 3},
+ {"tobinary", BindWith5Args(&GrpcTool::ToBinary), 2, 3},
};
void Usage(const grpc::string& msg) {
@@ -185,9 +224,9 @@ void Usage(const grpc::string& msg) {
" 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 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());
@@ -414,18 +453,229 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
grpc::string request_text;
grpc::string server_address(argv[0]);
grpc::string method_name(argv[1]);
+ grpc::string formatted_method_name;
+ std::unique_ptr<ProtoFileParser> parser;
+ grpc::string serialized_request_proto;
+ bool print_mode = false;
+
+ std::shared_ptr<grpc::Channel> channel =
+ FLAGS_remotedb
+ ? grpc::CreateChannel(server_address, cred.GetCredentials())
+ : nullptr;
+
+ parser.reset(new grpc::testing::ProtoFileParser(channel, FLAGS_proto_path,
+ FLAGS_protofiles));
+
+ if (FLAGS_binary_input) {
+ formatted_method_name = method_name;
+ } else {
+ formatted_method_name = parser->GetFormattedMethodName(method_name);
+ }
+
+ if (parser->HasError()) {
+ return false;
+ }
+
+ if (parser->IsStreaming(method_name, true /* is_request */)) {
+ std::istream* input_stream;
+ std::ifstream input_file;
+
+ if (argc == 3) {
+ request_text = argv[2];
+ }
+
+ std::multimap<grpc::string, grpc::string> client_metadata;
+ ParseMetadataFlag(&client_metadata);
+ PrintMetadata(client_metadata, "Sending client initial metadata:");
+
+ CliCall call(channel, formatted_method_name, client_metadata);
+
+ if (FLAGS_infile.empty()) {
+ if (isatty(fileno(stdin))) {
+ print_mode = true;
+ fprintf(stderr, "reading streaming request message from stdin...\n");
+ }
+ input_stream = &std::cin;
+ } else {
+ input_file.open(FLAGS_infile, std::ios::in | std::ios::binary);
+ input_stream = &input_file;
+ }
+
+ gpr_mu parser_mu;
+ gpr_mu_init(&parser_mu);
+ std::thread read_thread(ReadResponse, &call, method_name, callback,
+ parser.get(), &parser_mu, print_mode);
+
+ std::stringstream request_ss;
+ grpc::string line;
+ while (!request_text.empty() ||
+ (!input_stream->eof() && getline(*input_stream, line))) {
+ if (!request_text.empty()) {
+ if (FLAGS_binary_input) {
+ serialized_request_proto = request_text;
+ request_text.clear();
+ } else {
+ gpr_mu_lock(&parser_mu);
+ serialized_request_proto = parser->GetSerializedProtoFromMethod(
+ method_name, request_text, true /* is_request */);
+ request_text.clear();
+ if (parser->HasError()) {
+ if (print_mode) {
+ fprintf(stderr, "Failed to parse request.\n");
+ }
+ gpr_mu_unlock(&parser_mu);
+ continue;
+ }
+ gpr_mu_unlock(&parser_mu);
+ }
+
+ call.WriteAndWait(serialized_request_proto);
+ if (print_mode) {
+ fprintf(stderr, "Request sent.\n");
+ }
+ } else {
+ if (line.length() == 0) {
+ request_text = request_ss.str();
+ request_ss.str(grpc::string());
+ request_ss.clear();
+ } else {
+ request_ss << line << ' ';
+ }
+ }
+ }
+ if (input_file.is_open()) {
+ input_file.close();
+ }
+
+ call.WritesDoneAndWait();
+ read_thread.join();
+
+ std::multimap<grpc::string_ref, grpc::string_ref> server_trailing_metadata;
+ Status status = call.Finish(&server_trailing_metadata);
+ PrintMetadata(server_trailing_metadata,
+ "Received trailing metadata from server:");
+
+ if (status.ok()) {
+ fprintf(stderr, "Stream RPC succeeded with OK status\n");
+ return true;
+ } else {
+ fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
+ status.error_code(), status.error_message().c_str());
+ return false;
+ }
+
+ } else { // parser->IsStreaming(method_name, true /* is_request */)
+ 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(fileno(stdin))) {
+ 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();
+ }
+
+ if (FLAGS_binary_input) {
+ serialized_request_proto = request_text;
+ // formatted_method_name = method_name;
+ } else {
+ // formatted_method_name = parser->GetFormattedMethodName(method_name);
+ 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:");
+
+ CliCall call(channel, formatted_method_name, client_metadata);
+ call.Write(serialized_request_proto);
+ call.WritesDone();
+
+ for (bool receive_initial_metadata = true; call.Read(
+ &serialized_response_proto,
+ receive_initial_metadata ? &server_initial_metadata : nullptr);
+ receive_initial_metadata = false) {
+ if (!FLAGS_binary_output) {
+ serialized_response_proto = parser->GetTextFormatFromMethod(
+ method_name, serialized_response_proto, false /* is_request */);
+ if (parser->HasError()) {
+ return false;
+ }
+ }
+ if (receive_initial_metadata) {
+ PrintMetadata(server_initial_metadata,
+ "Received initial metadata from server:");
+ }
+ if (!callback(serialized_response_proto)) {
+ return false;
+ }
+ }
+ Status status = call.Finish(&server_trailing_metadata);
+ if (status.ok()) {
+ fprintf(stderr, "Rpc succeeded with OK status\n");
+ return true;
+ } else {
+ fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
+ status.error_code(), status.error_message().c_str());
+ return false;
+ }
+ }
+ GPR_UNREACHABLE_CODE(return false);
+}
+
+bool GrpcTool::ParseMessage(int argc, const char** argv,
+ const CliCredentials& cred,
+ GrpcToolOutputCallback callback) {
+ CommandUsage(
+ "Parse message\n"
+ " grpc_cli parse <address> <type> [<message>]\n"
+ " <address> ; host:port\n"
+ " <type> ; Protocol buffer type name\n"
+ " <message> ; Text protobuffer (overrides --infile)\n"
+ " --protofiles ; Comma separated proto files used as a"
+ " fallback when parsing request/response\n"
+ " --proto_path ; The search path of proto files, valid"
+ " only when --protofiles is given\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 message_text;
+ grpc::string server_address(argv[0]);
+ grpc::string type_name(argv[1]);
std::unique_ptr<grpc::testing::ProtoFileParser> parser;
grpc::string serialized_request_proto;
if (argc == 3) {
- request_text = argv[2];
+ message_text = argv[2];
if (!FLAGS_infile.empty()) {
- fprintf(stderr, "warning: request given in argv, ignoring --infile\n");
+ fprintf(stderr, "warning: message given in argv, ignoring --infile.\n");
}
} else {
std::stringstream input_stream;
if (FLAGS_infile.empty()) {
- if (isatty(STDIN_FILENO)) {
+ if (isatty(fileno(stdin))) {
fprintf(stderr, "reading request message from stdin...\n");
}
input_stream << std::cin.rdbuf();
@@ -434,12 +684,12 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
input_stream << input_file.rdbuf();
input_file.close();
}
- request_text = input_stream.str();
+ message_text = input_stream.str();
}
- std::shared_ptr<grpc::Channel> channel =
- grpc::CreateChannel(server_address, cred.GetCredentials());
if (!FLAGS_binary_input || !FLAGS_binary_output) {
+ std::shared_ptr<grpc::Channel> channel =
+ grpc::CreateChannel(server_address, cred.GetCredentials());
parser.reset(
new grpc::testing::ProtoFileParser(FLAGS_remotedb ? channel : nullptr,
FLAGS_proto_path, FLAGS_protofiles));
@@ -449,49 +699,64 @@ bool GrpcTool::CallMethod(int argc, const char** argv,
}
if (FLAGS_binary_input) {
- serialized_request_proto = request_text;
+ serialized_request_proto = message_text;
} else {
- serialized_request_proto = parser->GetSerializedProtoFromMethod(
- method_name, request_text, true /* is_request */);
+ serialized_request_proto =
+ parser->GetSerializedProtoFromMessageType(type_name, message_text);
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;
- }
+ if (FLAGS_binary_output) {
+ output_ss << serialized_request_proto;
} else {
- fprintf(stderr, "Rpc failed with status code %d, error message: %s\n",
- status.error_code(), status.error_message().c_str());
+ grpc::string output_text = parser->GetTextFormatFromMessageType(
+ type_name, serialized_request_proto);
+ if (parser->HasError()) {
+ return false;
+ }
+ output_ss << output_text << std::endl;
}
return callback(output_ss.str());
}
+bool GrpcTool::ToText(int argc, const char** argv, const CliCredentials& cred,
+ GrpcToolOutputCallback callback) {
+ CommandUsage(
+ "Convert binary message to text\n"
+ " grpc_cli totext <protofiles> <type>\n"
+ " <protofiles> ; Comma separated list of proto files\n"
+ " <type> ; Protocol buffer type name\n"
+ " --proto_path ; The search path of proto files\n"
+ " --infile ; Input filename (defaults to stdin)\n"
+ " --outfile ; Output filename (defaults to stdout)\n");
+
+ FLAGS_protofiles = argv[0];
+ FLAGS_remotedb = false;
+ FLAGS_binary_input = true;
+ FLAGS_binary_output = false;
+ return ParseMessage(argc, argv, cred, callback);
+}
+
+bool GrpcTool::ToBinary(int argc, const char** argv, const CliCredentials& cred,
+ GrpcToolOutputCallback callback) {
+ CommandUsage(
+ "Convert text message to binary\n"
+ " grpc_cli tobinary <protofiles> <type> [<message>]\n"
+ " <protofiles> ; Comma separated list of proto files\n"
+ " <type> ; Protocol buffer type name\n"
+ " --proto_path ; The search path of proto files\n"
+ " --infile ; Input filename (defaults to stdin)\n"
+ " --outfile ; Output filename (defaults to stdout)\n");
+
+ FLAGS_protofiles = argv[0];
+ FLAGS_remotedb = false;
+ FLAGS_binary_input = false;
+ FLAGS_binary_output = true;
+ return ParseMessage(argc, argv, cred, callback);
+}
+
} // namespace testing
} // namespace grpc
diff --git a/test/cpp/util/grpc_tool_test.cc b/test/cpp/util/grpc_tool_test.cc
index bad1579f11..26e2b1f502 100644
--- a/test/cpp/util/grpc_tool_test.cc
+++ b/test/cpp/util/grpc_tool_test.cc
@@ -86,20 +86,30 @@ using grpc::testing::EchoResponse;
" rpc Echo(grpc.testing.EchoRequest) returns (grpc.testing.EchoResponse) " \
"{}\n"
+#define ECHO_RESPONSE_MESSAGE \
+ "message: \"echo\"\n" \
+ "param {\n" \
+ " host: \"localhost\"\n" \
+ " peer: \"peer\"\n" \
+ "}\n\n"
+
namespace grpc {
namespace testing {
+DECLARE_bool(binary_input);
+DECLARE_bool(binary_output);
DECLARE_bool(l);
namespace {
-class TestCliCredentials GRPC_FINAL : public grpc::testing::CliCredentials {
+const int kNumResponseStreamsMsgs = 3;
+
+class TestCliCredentials final : public grpc::testing::CliCredentials {
public:
- std::shared_ptr<grpc::ChannelCredentials> GetCredentials() const
- GRPC_OVERRIDE {
+ std::shared_ptr<grpc::ChannelCredentials> GetCredentials() const override {
return InsecureChannelCredentials();
}
- const grpc::string GetCredentialUsage() const GRPC_OVERRIDE { return ""; }
+ const grpc::string GetCredentialUsage() const override { return ""; }
};
bool PrintStream(std::stringstream* ss, const grpc::string& output) {
@@ -113,12 +123,10 @@ size_t ArraySize(T& a) {
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))));
}
-} // namespame
-
class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
public:
Status Echo(ServerContext* context, const EchoRequest* request,
- EchoResponse* response) GRPC_OVERRIDE {
+ EchoResponse* response) override {
if (!context->client_metadata().empty()) {
for (std::multimap<grpc::string_ref, grpc::string_ref>::const_iterator
iter = context->client_metadata().begin();
@@ -131,8 +139,75 @@ class TestServiceImpl : public ::grpc::testing::EchoTestService::Service {
response->set_message(request->message());
return Status::OK;
}
+
+ Status RequestStream(ServerContext* context,
+ ServerReader<EchoRequest>* reader,
+ EchoResponse* response) override {
+ EchoRequest request;
+ response->set_message("");
+ 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");
+ while (reader->Read(&request)) {
+ response->mutable_message()->append(request.message());
+ }
+
+ return Status::OK;
+ }
+
+ Status ResponseStream(ServerContext* context, const EchoRequest* request,
+ ServerWriter<EchoResponse>* writer) 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");
+
+ EchoResponse response;
+ for (int i = 0; i < kNumResponseStreamsMsgs; i++) {
+ response.set_message(request->message() + grpc::to_string(i));
+ writer->Write(response);
+ }
+
+ return Status::OK;
+ }
+
+ Status BidiStream(
+ ServerContext* context,
+ ServerReaderWriter<EchoResponse, EchoRequest>* stream) override {
+ EchoRequest request;
+ EchoResponse response;
+ 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");
+
+ while (stream->Read(&request)) {
+ response.set_message(request.message());
+ stream->Write(response);
+ }
+
+ return Status::OK;
+ }
};
+} // namespace
+
class GrpcToolTest : public ::testing::Test {
protected:
GrpcToolTest() {}
@@ -339,6 +414,173 @@ TEST_F(GrpcToolTest, CallCommand) {
ShutdownServer();
}
+TEST_F(GrpcToolTest, CallCommandRequestStream) {
+ // Test input: grpc_cli call localhost:<port> RequestStream "message:
+ // 'Hello0'"
+ std::stringstream output_stream;
+
+ const grpc::string server_address = SetUpServer();
+ const char* argv[] = {"grpc_cli", "call", server_address.c_str(),
+ "RequestStream", "message: 'Hello0'"};
+
+ // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n"
+ std::streambuf* orig = std::cin.rdbuf();
+ std::istringstream ss("message: 'Hello1'\n\n message: 'Hello2'\n\n");
+ std::cin.rdbuf(ss.rdbuf());
+
+ EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+
+ // Expected output: "message: \"Hello0Hello1Hello2\""
+ EXPECT_TRUE(NULL != strstr(output_stream.str().c_str(),
+ "message: \"Hello0Hello1Hello2\""));
+ std::cin.rdbuf(orig);
+ ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, CallCommandRequestStreamWithBadRequest) {
+ // Test input: grpc_cli call localhost:<port> RequestStream "message:
+ // 'Hello0'"
+ std::stringstream output_stream;
+
+ const grpc::string server_address = SetUpServer();
+ const char* argv[] = {"grpc_cli", "call", server_address.c_str(),
+ "RequestStream", "message: 'Hello0'"};
+
+ // Mock std::cin input "bad_field: 'Hello1'\n\n message: 'Hello2'\n\n"
+ std::streambuf* orig = std::cin.rdbuf();
+ std::istringstream ss("bad_field: 'Hello1'\n\n message: 'Hello2'\n\n");
+ std::cin.rdbuf(ss.rdbuf());
+
+ EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+
+ // Expected output: "message: \"Hello0Hello2\""
+ EXPECT_TRUE(NULL !=
+ strstr(output_stream.str().c_str(), "message: \"Hello0Hello2\""));
+ std::cin.rdbuf(orig);
+ ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, CallCommandResponseStream) {
+ // Test input: grpc_cli call localhost:<port> ResponseStream "message:
+ // 'Hello'"
+ std::stringstream output_stream;
+
+ const grpc::string server_address = SetUpServer();
+ const char* argv[] = {"grpc_cli", "call", server_address.c_str(),
+ "ResponseStream", "message: 'Hello'"};
+
+ EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+
+ // Expected output: "message: \"Hello{n}\""
+ for (int i = 0; i < kNumResponseStreamsMsgs; i++) {
+ grpc::string expected_response_text =
+ "message: \"Hello" + grpc::to_string(i) + "\"\n";
+ EXPECT_TRUE(NULL != strstr(output_stream.str().c_str(),
+ expected_response_text.c_str()));
+ }
+
+ ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, CallCommandBidiStream) {
+ // Test input: grpc_cli call localhost:<port> BidiStream "message: 'Hello0'"
+ std::stringstream output_stream;
+
+ const grpc::string server_address = SetUpServer();
+ const char* argv[] = {"grpc_cli", "call", server_address.c_str(),
+ "BidiStream", "message: 'Hello0'"};
+
+ // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n"
+ std::streambuf* orig = std::cin.rdbuf();
+ std::istringstream ss("message: 'Hello1'\n\n message: 'Hello2'\n\n");
+ std::cin.rdbuf(ss.rdbuf());
+
+ EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+
+ // Expected output: "message: \"Hello0\"\nmessage: \"Hello1\"\nmessage:
+ // \"Hello2\"\n\n"
+ EXPECT_TRUE(NULL != strstr(output_stream.str().c_str(),
+ "message: \"Hello0\"\nmessage: "
+ "\"Hello1\"\nmessage: \"Hello2\"\n"));
+ std::cin.rdbuf(orig);
+ ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, CallCommandBidiStreamWithBadRequest) {
+ // Test input: grpc_cli call localhost:<port> BidiStream "message: 'Hello0'"
+ std::stringstream output_stream;
+
+ const grpc::string server_address = SetUpServer();
+ const char* argv[] = {"grpc_cli", "call", server_address.c_str(),
+ "BidiStream", "message: 'Hello0'"};
+
+ // Mock std::cin input "message: 'Hello1'\n\n message: 'Hello2'\n\n"
+ std::streambuf* orig = std::cin.rdbuf();
+ std::istringstream ss("message: 1.0\n\n message: 'Hello2'\n\n");
+ std::cin.rdbuf(ss.rdbuf());
+
+ EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+
+ // Expected output: "message: \"Hello0\"\nmessage: \"Hello1\"\nmessage:
+ // \"Hello2\"\n\n"
+ EXPECT_TRUE(NULL != strstr(output_stream.str().c_str(),
+ "message: \"Hello0\"\nmessage: \"Hello2\"\n"));
+ std::cin.rdbuf(orig);
+
+ ShutdownServer();
+}
+
+TEST_F(GrpcToolTest, ParseCommand) {
+ // Test input "grpc_cli parse localhost:<port> grpc.testing.EchoResponse
+ // ECHO_RESPONSE_MESSAGE"
+ std::stringstream output_stream;
+ std::stringstream binary_output_stream;
+
+ const grpc::string server_address = SetUpServer();
+ const char* argv[] = {"grpc_cli", "parse", server_address.c_str(),
+ "grpc.testing.EchoResponse", ECHO_RESPONSE_MESSAGE};
+
+ FLAGS_binary_input = false;
+ FLAGS_binary_output = false;
+ EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+ // Expected output: ECHO_RESPONSE_MESSAGE
+ EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE));
+
+ // Parse text message to binary message and then parse it back to text message
+ output_stream.str(grpc::string());
+ output_stream.clear();
+ FLAGS_binary_output = true;
+ EXPECT_TRUE(0 == GrpcToolMainLib(ArraySize(argv), argv, TestCliCredentials(),
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+ grpc::string binary_data = output_stream.str();
+ output_stream.str(grpc::string());
+ output_stream.clear();
+ argv[4] = binary_data.c_str();
+ FLAGS_binary_input = true;
+ FLAGS_binary_output = false;
+ EXPECT_TRUE(0 == GrpcToolMainLib(5, argv, TestCliCredentials(),
+ std::bind(PrintStream, &output_stream,
+ std::placeholders::_1)));
+
+ // Expected output: ECHO_RESPONSE_MESSAGE
+ EXPECT_TRUE(0 == strcmp(output_stream.str().c_str(), ECHO_RESPONSE_MESSAGE));
+
+ ShutdownServer();
+}
+
TEST_F(GrpcToolTest, TooFewArguments) {
// Test input "grpc_cli call Echo"
std::stringstream output_stream;
diff --git a/test/cpp/util/metrics_server.h b/test/cpp/util/metrics_server.h
index aa9bfed23d..4f1e393a63 100644
--- a/test/cpp/util/metrics_server.h
+++ b/test/cpp/util/metrics_server.h
@@ -83,13 +83,13 @@ class QpsGauge {
std::mutex num_queries_mu_;
};
-class MetricsServiceImpl GRPC_FINAL : public MetricsService::Service {
+class MetricsServiceImpl final : public MetricsService::Service {
public:
grpc::Status GetAllGauges(ServerContext* context, const EmptyMessage* request,
- ServerWriter<GaugeResponse>* writer) GRPC_OVERRIDE;
+ ServerWriter<GaugeResponse>* writer) override;
grpc::Status GetGauge(ServerContext* context, const GaugeRequest* request,
- GaugeResponse* response) GRPC_OVERRIDE;
+ GaugeResponse* response) override;
// Create a QpsGauge with name 'name'. is_present is set to true if the Gauge
// is already present in the map.
diff --git a/test/cpp/util/proto_file_parser.cc b/test/cpp/util/proto_file_parser.cc
index 98dd3f14ad..5dd1b00e8a 100644
--- a/test/cpp/util/proto_file_parser.cc
+++ b/test/cpp/util/proto_file_parser.cc
@@ -61,7 +61,7 @@ class ErrorPrinter : public protobuf::compiler::MultiFileErrorCollector {
explicit ErrorPrinter(ProtoFileParser* parser) : parser_(parser) {}
void AddError(const grpc::string& filename, int line, int column,
- const grpc::string& message) GRPC_OVERRIDE {
+ const grpc::string& message) override {
std::ostringstream oss;
oss << "error " << filename << " " << line << " " << column << " "
<< message << "\n";
@@ -69,7 +69,7 @@ class ErrorPrinter : public protobuf::compiler::MultiFileErrorCollector {
}
void AddWarning(const grpc::string& filename, int line, int column,
- const grpc::string& message) GRPC_OVERRIDE {
+ const grpc::string& message) override {
std::cerr << "warning " << filename << " " << line << " " << column << " "
<< message << std::endl;
}
@@ -81,7 +81,8 @@ class ErrorPrinter : public protobuf::compiler::MultiFileErrorCollector {
ProtoFileParser::ProtoFileParser(std::shared_ptr<grpc::Channel> channel,
const grpc::string& proto_path,
const grpc::string& protofiles)
- : has_error_(false) {
+ : has_error_(false),
+ dynamic_factory_(new protobuf::DynamicMessageFactory()) {
std::vector<grpc::string> service_list;
if (channel) {
reflection_db_.reset(new grpc::ProtoReflectionDescriptorDatabase(channel));
@@ -127,7 +128,6 @@ ProtoFileParser::ProtoFileParser(std::shared_ptr<grpc::Channel> channel,
}
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++) {
if (known_services.find(*it) == known_services.end()) {
@@ -144,6 +144,11 @@ ProtoFileParser::~ProtoFileParser() {}
grpc::string ProtoFileParser::GetFullMethodName(const grpc::string& method) {
has_error_ = false;
+
+ if (known_methods_.find(method) != known_methods_.end()) {
+ return known_methods_[method];
+ }
+
const protobuf::MethodDescriptor* method_descriptor = nullptr;
for (auto it = service_desc_list_.begin(); it != service_desc_list_.end();
it++) {
@@ -169,22 +174,24 @@ grpc::string ProtoFileParser::GetFullMethodName(const grpc::string& method) {
return "";
}
+ known_methods_[method] = method_descriptor->full_name();
+
return method_descriptor->full_name();
}
-grpc::string ProtoFileParser::GetFormatedMethodName(
+grpc::string ProtoFileParser::GetFormattedMethodName(
const grpc::string& method) {
has_error_ = false;
- grpc::string formated_method_name = GetFullMethodName(method);
+ grpc::string formatted_method_name = GetFullMethodName(method);
if (has_error_) {
return "";
}
- size_t last_dot = formated_method_name.find_last_of('.');
+ size_t last_dot = formatted_method_name.find_last_of('.');
if (last_dot != grpc::string::npos) {
- formated_method_name[last_dot] = '/';
+ formatted_method_name[last_dot] = '/';
}
- formated_method_name.insert(formated_method_name.begin(), '/');
- return formated_method_name;
+ formatted_method_name.insert(formatted_method_name.begin(), '/');
+ return formatted_method_name;
}
grpc::string ProtoFileParser::GetMessageTypeFromMethod(
@@ -205,6 +212,25 @@ grpc::string ProtoFileParser::GetMessageTypeFromMethod(
: method_desc->output_type()->full_name();
}
+bool ProtoFileParser::IsStreaming(const grpc::string& method, bool is_request) {
+ has_error_ = false;
+
+ grpc::string full_method_name = GetFullMethodName(method);
+ if (has_error_) {
+ return false;
+ }
+
+ const protobuf::MethodDescriptor* method_desc =
+ desc_pool_->FindMethodByName(full_method_name);
+ if (!method_desc) {
+ LogError("Method not found");
+ return false;
+ }
+
+ return is_request ? method_desc->client_streaming()
+ : method_desc->server_streaming();
+}
+
grpc::string ProtoFileParser::GetSerializedProtoFromMethod(
const grpc::string& method, const grpc::string& text_format_proto,
bool is_request) {
diff --git a/test/cpp/util/proto_file_parser.h b/test/cpp/util/proto_file_parser.h
index eda3991e72..23d311ef8f 100644
--- a/test/cpp/util/proto_file_parser.h
+++ b/test/cpp/util/proto_file_parser.h
@@ -64,9 +64,9 @@ class ProtoFileParser {
// 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
+ // Formatted 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 GetFormattedMethodName(const grpc::string& method);
grpc::string GetSerializedProtoFromMethod(
const grpc::string& method, const grpc::string& text_format_proto,
@@ -84,6 +84,8 @@ class ProtoFileParser {
const grpc::string& message_type_name,
const grpc::string& serialized_proto);
+ bool IsStreaming(const grpc::string& method, bool is_request);
+
bool HasError() const { return has_error_; }
void LogError(const grpc::string& error_msg);
@@ -104,6 +106,7 @@ class ProtoFileParser {
std::unique_ptr<protobuf::DynamicMessageFactory> dynamic_factory_;
std::unique_ptr<grpc::protobuf::Message> request_prototype_;
std::unique_ptr<grpc::protobuf::Message> response_prototype_;
+ std::unordered_map<grpc::string, grpc::string> known_methods_;
std::vector<const protobuf::ServiceDescriptor*> service_desc_list_;
};
diff --git a/test/cpp/util/proto_reflection_descriptor_database.h b/test/cpp/util/proto_reflection_descriptor_database.h
index dfa36044d9..259277ebbe 100644
--- a/test/cpp/util/proto_reflection_descriptor_database.h
+++ b/test/cpp/util/proto_reflection_descriptor_database.h
@@ -38,14 +38,9 @@
#include <unordered_set>
#include <vector>
-// GRPC_NO_GENERATED_CODE indicates generated pb files should not be used
-#ifdef GRPC_NO_GENERATED_CODE
-#include "src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h"
-#else
-#include <grpc++/ext/reflection.grpc.pb.h>
-#endif // GRPC_NO_GENERATED_CODE
#include <grpc++/grpc++.h>
#include <grpc++/impl/codegen/config_protobuf.h>
+#include "src/proto/grpc/reflection/v1alpha/reflection.grpc.pb.h"
namespace grpc {
@@ -67,14 +62,13 @@ class ProtoReflectionDescriptorDatabase : public protobuf::DescriptorDatabase {
// Find a file by file name. Fills in in *output and returns true if found.
// Otherwise, returns false, leaving the contents of *output undefined.
bool FindFileByName(const string& filename,
- protobuf::FileDescriptorProto* output) GRPC_OVERRIDE;
+ protobuf::FileDescriptorProto* output) override;
// Find the file that declares the given fully-qualified symbol name.
// If found, fills in *output and returns true, otherwise returns false
// and leaves *output undefined.
bool FindFileContainingSymbol(const string& symbol_name,
- protobuf::FileDescriptorProto* output)
- GRPC_OVERRIDE;
+ protobuf::FileDescriptorProto* output) override;
// Find the file which defines an extension extending the given message type
// with the given field number. If found, fills in *output and returns true,
@@ -82,7 +76,7 @@ class ProtoReflectionDescriptorDatabase : public protobuf::DescriptorDatabase {
// must be a fully-qualified type name.
bool FindFileContainingExtension(
const string& containing_type, int field_number,
- protobuf::FileDescriptorProto* output) GRPC_OVERRIDE;
+ protobuf::FileDescriptorProto* output) override;
// Finds the tag numbers used by all known extensions of
// extendee_type, and appends them to output in an undefined
@@ -92,7 +86,7 @@ class ProtoReflectionDescriptorDatabase : public protobuf::DescriptorDatabase {
// numbers. Returns true if the search was successful, otherwise
// returns false and leaves output unchanged.
bool FindAllExtensionNumbers(const string& extendee_type,
- std::vector<int>* output) GRPC_OVERRIDE;
+ std::vector<int>* output) override;
// Provide a list of full names of registered services
bool GetServices(std::vector<grpc::string>* output);
diff --git a/test/cpp/util/slice_test.cc b/test/cpp/util/slice_test.cc
index 45799ae157..8926fb7d5a 100644
--- a/test/cpp/util/slice_test.cc
+++ b/test/cpp/util/slice_test.cc
@@ -33,7 +33,7 @@
#include <grpc++/support/slice.h>
-#include <grpc/support/slice.h>
+#include <grpc/slice.h>
#include <gtest/gtest.h>
namespace grpc {
@@ -51,15 +51,15 @@ class SliceTest : public ::testing::Test {
};
TEST_F(SliceTest, Steal) {
- gpr_slice s = gpr_slice_from_copied_string(kContent);
+ grpc_slice s = grpc_slice_from_copied_string(kContent);
Slice spp(s, Slice::STEAL_REF);
CheckSlice(spp, kContent);
}
TEST_F(SliceTest, Add) {
- gpr_slice s = gpr_slice_from_copied_string(kContent);
+ grpc_slice s = grpc_slice_from_copied_string(kContent);
Slice spp(s, Slice::ADD_REF);
- gpr_slice_unref(s);
+ grpc_slice_unref(s);
CheckSlice(spp, kContent);
}
@@ -69,13 +69,13 @@ TEST_F(SliceTest, Empty) {
}
TEST_F(SliceTest, Cslice) {
- gpr_slice s = gpr_slice_from_copied_string(kContent);
+ grpc_slice s = grpc_slice_from_copied_string(kContent);
Slice spp(s, Slice::STEAL_REF);
CheckSlice(spp, kContent);
- gpr_slice c_slice = spp.c_slice();
- EXPECT_EQ(GPR_SLICE_START_PTR(s), GPR_SLICE_START_PTR(c_slice));
- EXPECT_EQ(GPR_SLICE_END_PTR(s), GPR_SLICE_END_PTR(c_slice));
- gpr_slice_unref(c_slice);
+ grpc_slice c_slice = spp.c_slice();
+ EXPECT_EQ(GRPC_SLICE_START_PTR(s), GRPC_SLICE_START_PTR(c_slice));
+ EXPECT_EQ(GRPC_SLICE_END_PTR(s), GRPC_SLICE_END_PTR(c_slice));
+ grpc_slice_unref(c_slice);
}
} // namespace
diff --git a/test/cpp/util/test_credentials_provider.cc b/test/cpp/util/test_credentials_provider.cc
index ca15f29795..909b02a701 100644
--- a/test/cpp/util/test_credentials_provider.cc
+++ b/test/cpp/util/test_credentials_provider.cc
@@ -34,44 +34,28 @@
#include "test/cpp/util/test_credentials_provider.h"
+#include <mutex>
#include <unordered_map>
-#include <grpc++/impl/sync.h>
#include <grpc/support/log.h>
#include <grpc/support/sync.h>
#include "test/core/end2end/data/ssl_test_data.h"
namespace grpc {
+namespace testing {
namespace {
-using grpc::testing::CredentialTypeProvider;
-
-// Provide test credentials. Thread-safe.
-class CredentialsProvider {
- public:
- virtual ~CredentialsProvider() {}
-
- virtual void AddSecureType(
- const grpc::string& type,
- std::unique_ptr<CredentialTypeProvider> type_provider) = 0;
- virtual std::shared_ptr<ChannelCredentials> GetChannelCredentials(
- const grpc::string& type, ChannelArguments* args) = 0;
- virtual std::shared_ptr<ServerCredentials> GetServerCredentials(
- const grpc::string& type) = 0;
- virtual std::vector<grpc::string> GetSecureCredentialsTypeList() = 0;
-};
-
class DefaultCredentialsProvider : public CredentialsProvider {
public:
- ~DefaultCredentialsProvider() GRPC_OVERRIDE {}
+ ~DefaultCredentialsProvider() override {}
- void AddSecureType(const grpc::string& type,
- std::unique_ptr<CredentialTypeProvider> type_provider)
- GRPC_OVERRIDE {
+ void AddSecureType(
+ const grpc::string& type,
+ std::unique_ptr<CredentialTypeProvider> type_provider) override {
// This clobbers any existing entry for type, except the defaults, which
// can't be clobbered.
- grpc::unique_lock<grpc::mutex> lock(mu_);
+ std::unique_lock<std::mutex> lock(mu_);
auto it = std::find(added_secure_type_names_.begin(),
added_secure_type_names_.end(), type);
if (it == added_secure_type_names_.end()) {
@@ -84,7 +68,7 @@ class DefaultCredentialsProvider : public CredentialsProvider {
}
std::shared_ptr<ChannelCredentials> GetChannelCredentials(
- const grpc::string& type, ChannelArguments* args) GRPC_OVERRIDE {
+ const grpc::string& type, ChannelArguments* args) override {
if (type == grpc::testing::kInsecureCredentialsType) {
return InsecureChannelCredentials();
} else if (type == grpc::testing::kTlsCredentialsType) {
@@ -92,7 +76,7 @@ class DefaultCredentialsProvider : public CredentialsProvider {
args->SetSslTargetNameOverride("foo.test.google.fr");
return SslCredentials(ssl_opts);
} else {
- grpc::unique_lock<grpc::mutex> lock(mu_);
+ std::unique_lock<std::mutex> lock(mu_);
auto it(std::find(added_secure_type_names_.begin(),
added_secure_type_names_.end(), type));
if (it == added_secure_type_names_.end()) {
@@ -105,7 +89,7 @@ class DefaultCredentialsProvider : public CredentialsProvider {
}
std::shared_ptr<ServerCredentials> GetServerCredentials(
- const grpc::string& type) GRPC_OVERRIDE {
+ const grpc::string& type) override {
if (type == grpc::testing::kInsecureCredentialsType) {
return InsecureServerCredentials();
} else if (type == grpc::testing::kTlsCredentialsType) {
@@ -116,7 +100,7 @@ class DefaultCredentialsProvider : public CredentialsProvider {
ssl_opts.pem_key_cert_pairs.push_back(pkcp);
return SslServerCredentials(ssl_opts);
} else {
- grpc::unique_lock<grpc::mutex> lock(mu_);
+ std::unique_lock<std::mutex> lock(mu_);
auto it(std::find(added_secure_type_names_.begin(),
added_secure_type_names_.end(), type));
if (it == added_secure_type_names_.end()) {
@@ -127,10 +111,10 @@ class DefaultCredentialsProvider : public CredentialsProvider {
->GetServerCredentials();
}
}
- std::vector<grpc::string> GetSecureCredentialsTypeList() GRPC_OVERRIDE {
+ std::vector<grpc::string> GetSecureCredentialsTypeList() override {
std::vector<grpc::string> types;
types.push_back(grpc::testing::kTlsCredentialsType);
- grpc::unique_lock<grpc::mutex> lock(mu_);
+ std::unique_lock<std::mutex> lock(mu_);
for (auto it = added_secure_type_names_.begin();
it != added_secure_type_names_.end(); it++) {
types.push_back(*it);
@@ -139,43 +123,27 @@ class DefaultCredentialsProvider : public CredentialsProvider {
}
private:
- grpc::mutex mu_;
+ std::mutex mu_;
std::vector<grpc::string> added_secure_type_names_;
std::vector<std::unique_ptr<CredentialTypeProvider>>
added_secure_type_providers_;
};
-gpr_once g_once_init_provider = GPR_ONCE_INIT;
CredentialsProvider* g_provider = nullptr;
-void CreateDefaultProvider() { g_provider = new DefaultCredentialsProvider; }
-
-CredentialsProvider* GetProvider() {
- gpr_once_init(&g_once_init_provider, &CreateDefaultProvider);
- return g_provider;
-}
-
} // namespace
-namespace testing {
-
-void AddSecureType(const grpc::string& type,
- std::unique_ptr<CredentialTypeProvider> type_provider) {
- GetProvider()->AddSecureType(type, std::move(type_provider));
-}
-
-std::shared_ptr<ChannelCredentials> GetChannelCredentials(
- const grpc::string& type, ChannelArguments* args) {
- return GetProvider()->GetChannelCredentials(type, args);
-}
-
-std::shared_ptr<ServerCredentials> GetServerCredentials(
- const grpc::string& type) {
- return GetProvider()->GetServerCredentials(type);
+CredentialsProvider* GetCredentialsProvider() {
+ if (g_provider == nullptr) {
+ g_provider = new DefaultCredentialsProvider;
+ }
+ return g_provider;
}
-std::vector<grpc::string> GetSecureCredentialsTypeList() {
- return GetProvider()->GetSecureCredentialsTypeList();
+void SetCredentialsProvider(CredentialsProvider* provider) {
+ // For now, forbids overriding provider.
+ GPR_ASSERT(g_provider == nullptr);
+ g_provider = provider;
}
} // namespace testing
diff --git a/test/cpp/util/test_credentials_provider.h b/test/cpp/util/test_credentials_provider.h
index 1fb311e556..0bc52ebe4d 100644
--- a/test/cpp/util/test_credentials_provider.h
+++ b/test/cpp/util/test_credentials_provider.h
@@ -59,23 +59,39 @@ class CredentialTypeProvider {
virtual std::shared_ptr<ServerCredentials> GetServerCredentials() = 0;
};
-// Add a secure type in addition to the defaults above
-// (kInsecureCredentialsType, kTlsCredentialsType) that can be returned from the
-// functions below.
-void AddSecureType(const grpc::string& type,
- std::unique_ptr<CredentialTypeProvider> type_provider);
-
-// Provide channel credentials according to the given type. Alter the channel
-// arguments if needed.
-std::shared_ptr<ChannelCredentials> GetChannelCredentials(
- const grpc::string& type, ChannelArguments* args);
-
-// Provide server credentials according to the given type.
-std::shared_ptr<ServerCredentials> GetServerCredentials(
- const grpc::string& type);
-
-// Provide a list of secure credentials type.
-std::vector<grpc::string> GetSecureCredentialsTypeList();
+// Provide test credentials. Thread-safe.
+class CredentialsProvider {
+ public:
+ virtual ~CredentialsProvider() {}
+
+ // Add a secure type in addition to the defaults. The default provider has
+ // (kInsecureCredentialsType, kTlsCredentialsType).
+ virtual void AddSecureType(
+ const grpc::string& type,
+ std::unique_ptr<CredentialTypeProvider> type_provider) = 0;
+
+ // Provide channel credentials according to the given type. Alter the channel
+ // arguments if needed. Return nullptr if type is not registered.
+ virtual std::shared_ptr<ChannelCredentials> GetChannelCredentials(
+ const grpc::string& type, ChannelArguments* args) = 0;
+
+ // Provide server credentials according to the given type.
+ // Return nullptr if type is not registered.
+ virtual std::shared_ptr<ServerCredentials> GetServerCredentials(
+ const grpc::string& type) = 0;
+
+ // Provide a list of secure credentials type.
+ virtual std::vector<grpc::string> GetSecureCredentialsTypeList() = 0;
+};
+
+// Get the current provider. Create a default one if not set.
+// Not thread-safe.
+CredentialsProvider* GetCredentialsProvider();
+
+// Set the global provider. Takes ownership. The previous set provider will be
+// destroyed.
+// Not thread-safe.
+void SetCredentialsProvider(CredentialsProvider* provider);
} // namespace testing
} // namespace grpc