diff options
Diffstat (limited to 'test/cpp')
41 files changed, 1650 insertions, 793 deletions
diff --git a/test/cpp/end2end/async_end2end_test.cc b/test/cpp/end2end/async_end2end_test.cc index 45f5eb1ddd..6c7eae53a4 100644 --- a/test/cpp/end2end/async_end2end_test.cc +++ b/test/cpp/end2end/async_end2end_test.cc @@ -31,6 +31,7 @@ * */ +#include <cinttypes> #include <memory> #include <thread> @@ -207,12 +208,11 @@ class ServerBuilderSyncPluginDisabler : public ::grpc::ServerBuilderOption { public: void UpdateArguments(ChannelArguments* arg) GRPC_OVERRIDE {} - void UpdatePlugins( - std::map<grpc::string, std::unique_ptr<ServerBuilderPlugin>>* plugins) + void UpdatePlugins(std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) GRPC_OVERRIDE { auto plugin = plugins->begin(); while (plugin != plugins->end()) { - if ((*plugin).second->has_sync_methods()) { + if ((*plugin)->has_sync_methods()) { plugins->erase(plugin++); } else { plugin++; @@ -229,13 +229,17 @@ class TestScenario { credentials_type(creds_type), message_content(content) {} void Log() const { - gpr_log(GPR_INFO, - "Scenario: disable_blocking %d, credentials %s, message size %d", - disable_blocking, credentials_type.c_str(), message_content.size()); + gpr_log( + GPR_INFO, + "Scenario: disable_blocking %d, credentials %s, message size %" PRIuPTR, + disable_blocking, credentials_type.c_str(), message_content.size()); } bool disable_blocking; - const grpc::string credentials_type; - const grpc::string message_content; + // 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) + // manage vector insertion using a copy constructor + grpc::string credentials_type; + grpc::string message_content; }; class AsyncEnd2endTest : public ::testing::TestWithParam<TestScenario> { @@ -819,7 +823,7 @@ TEST_P(AsyncEnd2endTest, ServerCheckCancellation) { EXPECT_TRUE(srv_ctx.IsCancelled()); response_reader->Finish(&recv_response, &recv_status, tag(4)); - Verifier(GetParam().disable_blocking).Expect(4, false).Verify(cq_.get()); + Verifier(GetParam().disable_blocking).Expect(4, true).Verify(cq_.get()); EXPECT_EQ(StatusCode::CANCELLED, recv_status.error_code()); } @@ -881,7 +885,7 @@ TEST_P(AsyncEnd2endTest, UnimplementedRpc) { stub->AsyncUnimplemented(&cli_ctx, send_request, cq_.get())); response_reader->Finish(&recv_response, &recv_status, tag(4)); - Verifier(GetParam().disable_blocking).Expect(4, false).Verify(cq_.get()); + Verifier(GetParam().disable_blocking).Expect(4, true).Verify(cq_.get()); EXPECT_EQ(StatusCode::UNIMPLEMENTED, recv_status.error_code()); EXPECT_EQ("", recv_status.error_message()); @@ -939,7 +943,7 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest { // Client sends 3 messages (tags 3, 4 and 5) for (int tag_idx = 3; tag_idx <= 5; tag_idx++) { - send_request.set_message("Ping " + std::to_string(tag_idx)); + send_request.set_message("Ping " + grpc::to_string(tag_idx)); cli_stream->Write(send_request, tag(tag_idx)); Verifier(GetParam().disable_blocking) .Expect(tag_idx, true) @@ -1026,9 +1030,7 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest { // Client will see the cancellation cli_stream->Finish(&recv_status, tag(10)); - // TODO(sreek): The expectation here should be true. This is a bug (github - // issue #4972) - Verifier(GetParam().disable_blocking).Expect(10, false).Verify(cq_.get()); + Verifier(GetParam().disable_blocking).Expect(10, true).Verify(cq_.get()); EXPECT_FALSE(recv_status.ok()); EXPECT_EQ(::grpc::StatusCode::CANCELLED, recv_status.error_code()); } @@ -1107,7 +1109,7 @@ class AsyncEnd2endServerTryCancelTest : public AsyncEnd2endTest { // Server sends three messages (tags 3, 4 and 5) // But if want_done tag is true, we might also see tag 11 for (int tag_idx = 3; tag_idx <= 5; tag_idx++) { - send_response.set_message("Pong " + std::to_string(tag_idx)); + send_response.set_message("Pong " + grpc::to_string(tag_idx)); srv_stream.Write(send_response, tag(tag_idx)); // Note that we'll add something to the verifier and verify that // something was seen, but it might be tag 11 and not what we @@ -1398,9 +1400,9 @@ std::vector<TestScenario> CreateTestScenarios(bool test_disable_blocking, for (auto cred = credentials_types.begin(); cred != credentials_types.end(); ++cred) { for (auto msg = messages.begin(); msg != messages.end(); msg++) { - scenarios.push_back(TestScenario(false, *cred, *msg)); + scenarios.emplace_back(false, *cred, *msg); if (test_disable_blocking) { - scenarios.push_back(TestScenario(true, *cred, *msg)); + scenarios.emplace_back(true, *cred, *msg); } } } diff --git a/test/cpp/end2end/end2end_test.cc b/test/cpp/end2end/end2end_test.cc index f52aa52f39..354a59cedd 100644 --- a/test/cpp/end2end/end2end_test.cc +++ b/test/cpp/end2end/end2end_test.cc @@ -75,6 +75,8 @@ bool CheckIsLocalhost(const grpc::string& addr) { addr.substr(0, kIpv6.size()) == kIpv6; } +const char kTestCredsPluginErrorMsg[] = "Could not find plugin metadata."; + class TestMetadataCredentialsPlugin : public MetadataCredentialsPlugin { public: static const char kMetadataKey[]; @@ -99,7 +101,7 @@ class TestMetadataCredentialsPlugin : public MetadataCredentialsPlugin { metadata->insert(std::make_pair(kMetadataKey, metadata_value_)); return Status::OK; } else { - return Status(StatusCode::NOT_FOUND, "Could not find plugin metadata."); + return Status(StatusCode::NOT_FOUND, kTestCredsPluginErrorMsg); } } @@ -199,7 +201,10 @@ class TestScenario { credentials_type.c_str()); } bool use_proxy; - const grpc::string credentials_type; + // 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) + // manage vector insertion using a copy constructor + grpc::string credentials_type; }; class End2endTest : public ::testing::TestWithParam<TestScenario> { @@ -329,7 +334,7 @@ class End2endServerTryCancelTest : public End2endTest { // Send server_try_cancel value in the client metadata context.AddMetadata(kServerTryCancelRequest, - std::to_string(server_try_cancel)); + grpc::to_string(server_try_cancel)); auto stream = stub_->RequestStream(&context, &response); @@ -402,7 +407,7 @@ class End2endServerTryCancelTest : public End2endTest { // Send server_try_cancel in the client metadata context.AddMetadata(kServerTryCancelRequest, - std::to_string(server_try_cancel)); + grpc::to_string(server_try_cancel)); request.set_message("hello"); auto stream = stub_->ResponseStream(&context, request); @@ -413,7 +418,7 @@ class End2endServerTryCancelTest : public End2endTest { break; } EXPECT_EQ(response.message(), - request.message() + std::to_string(num_msgs_read)); + request.message() + grpc::to_string(num_msgs_read)); num_msgs_read++; } gpr_log(GPR_INFO, "Read %d messages", num_msgs_read); @@ -479,14 +484,14 @@ class End2endServerTryCancelTest : public End2endTest { // Send server_try_cancel in the client metadata context.AddMetadata(kServerTryCancelRequest, - std::to_string(server_try_cancel)); + grpc::to_string(server_try_cancel)); auto stream = stub_->BidiStream(&context); int num_msgs_read = 0; int num_msgs_sent = 0; while (num_msgs_sent < num_messages) { - request.set_message("hello " + std::to_string(num_msgs_sent)); + request.set_message("hello " + grpc::to_string(num_msgs_sent)); if (!stream->Write(request)) { break; } @@ -548,7 +553,7 @@ TEST_P(End2endServerTryCancelTest, RequestEchoServerCancel) { ClientContext context; context.AddMetadata(kServerTryCancelRequest, - std::to_string(CANCEL_BEFORE_PROCESSING)); + grpc::to_string(CANCEL_BEFORE_PROCESSING)); Status s = stub_->Echo(&context, request, &response); EXPECT_FALSE(s.ok()); EXPECT_EQ(grpc::StatusCode::CANCELLED, s.error_code()); @@ -1014,6 +1019,16 @@ TEST_P(ProxyEnd2endTest, SimpleRpc) { SendRpc(stub_.get(), 1, false); } +TEST_P(ProxyEnd2endTest, SimpleRpcWithEmptyMessages) { + ResetStub(); + EchoRequest request; + EchoResponse response; + + ClientContext context; + Status s = stub_->Echo(&context, request, &response); + EXPECT_TRUE(s.ok()); +} + TEST_P(ProxyEnd2endTest, MultipleRpcs) { ResetStub(); std::vector<std::thread*> threads; @@ -1318,6 +1333,7 @@ TEST_P(SecureEnd2endTest, NonBlockingAuthMetadataPluginFailure) { Status s = stub_->Echo(&context, request, &response); EXPECT_FALSE(s.ok()); EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED); + EXPECT_EQ(s.error_message(), kTestCredsPluginErrorMsg); } TEST_P(SecureEnd2endTest, NonBlockingAuthMetadataPluginAndProcessorSuccess) { @@ -1375,6 +1391,7 @@ TEST_P(SecureEnd2endTest, BlockingAuthMetadataPluginFailure) { Status s = stub_->Echo(&context, request, &response); EXPECT_FALSE(s.ok()); EXPECT_EQ(s.error_code(), StatusCode::UNAUTHENTICATED); + EXPECT_EQ(s.error_message(), kTestCredsPluginErrorMsg); } TEST_P(SecureEnd2endTest, ClientAuthContext) { @@ -1421,9 +1438,9 @@ std::vector<TestScenario> CreateTestScenarios(bool use_proxy, } for (auto it = credentials_types.begin(); it != credentials_types.end(); ++it) { - scenarios.push_back(TestScenario(false, *it)); + scenarios.emplace_back(false, *it); if (use_proxy) { - scenarios.push_back(TestScenario(true, *it)); + scenarios.emplace_back(true, *it); } } return scenarios; diff --git a/test/cpp/end2end/generic_end2end_test.cc b/test/cpp/end2end/generic_end2end_test.cc index d0cf6aea9d..57efa5fa17 100644 --- a/test/cpp/end2end/generic_end2end_test.cc +++ b/test/cpp/end2end/generic_end2end_test.cc @@ -38,7 +38,7 @@ #include <grpc++/create_channel.h> #include <grpc++/generic/async_generic_service.h> #include <grpc++/generic/generic_stub.h> -#include <grpc++/impl/proto_utils.h> +#include <grpc++/impl/codegen/proto_utils.h> #include <grpc++/server.h> #include <grpc++/server_builder.h> #include <grpc++/server_context.h> diff --git a/test/cpp/end2end/hybrid_end2end_test.cc b/test/cpp/end2end/hybrid_end2end_test.cc index 2c05db345b..7e0c0e8a7c 100644 --- a/test/cpp/end2end/hybrid_end2end_test.cc +++ b/test/cpp/end2end/hybrid_end2end_test.cc @@ -347,47 +347,50 @@ class HybridEnd2endTest : public ::testing::Test { } grpc::testing::UnimplementedService::Service unimplemented_service_; - std::vector<std::unique_ptr<ServerCompletionQueue> > cqs_; + std::vector<std::unique_ptr<ServerCompletionQueue>> cqs_; std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_; std::unique_ptr<Server> server_; std::ostringstream server_address_; }; TEST_F(HybridEnd2endTest, AsyncEcho) { - EchoTestService::WithAsyncMethod_Echo<TestServiceImpl> service; + typedef EchoTestService::WithAsyncMethod_Echo<TestServiceImpl> SType; + SType service; SetUpServer(&service, nullptr, nullptr); ResetStub(); - std::thread echo_handler_thread( - [this, &service] { HandleEcho(&service, cqs_[0].get(), false); }); + std::thread echo_handler_thread(HandleEcho<SType>, &service, cqs_[0].get(), + false); TestAllMethods(); echo_handler_thread.join(); } TEST_F(HybridEnd2endTest, AsyncEchoRequestStream) { - EchoTestService::WithAsyncMethod_RequestStream< - EchoTestService::WithAsyncMethod_Echo<TestServiceImpl> > - service; + typedef EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithAsyncMethod_Echo<TestServiceImpl>> + SType; + SType service; SetUpServer(&service, nullptr, nullptr); ResetStub(); - std::thread echo_handler_thread( - [this, &service] { HandleEcho(&service, cqs_[0].get(), false); }); - std::thread request_stream_handler_thread( - [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + std::thread echo_handler_thread(HandleEcho<SType>, &service, cqs_[0].get(), + false); + std::thread request_stream_handler_thread(HandleClientStreaming<SType>, + &service, cqs_[1].get()); TestAllMethods(); echo_handler_thread.join(); request_stream_handler_thread.join(); } TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream) { - EchoTestService::WithAsyncMethod_RequestStream< - EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > - service; + typedef EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>> + SType; + SType service; SetUpServer(&service, nullptr, nullptr); ResetStub(); - std::thread response_stream_handler_thread( - [this, &service] { HandleServerStreaming(&service, cqs_[0].get()); }); - std::thread request_stream_handler_thread( - [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + 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(); response_stream_handler_thread.join(); request_stream_handler_thread.join(); @@ -395,16 +398,17 @@ TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream) { // Add a second service with one sync method. TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream_SyncDupService) { - EchoTestService::WithAsyncMethod_RequestStream< - EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > - service; + typedef EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>> + SType; + SType service; TestServiceImplDupPkg dup_service; SetUpServer(&service, &dup_service, nullptr); ResetStub(); - std::thread response_stream_handler_thread( - [this, &service] { HandleServerStreaming(&service, cqs_[0].get()); }); - std::thread request_stream_handler_thread( - [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + 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(); response_stream_handler_thread.join(); @@ -413,18 +417,20 @@ TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream_SyncDupService) { // Add a second service with one async method. TEST_F(HybridEnd2endTest, AsyncRequestStreamResponseStream_AsyncDupService) { - EchoTestService::WithAsyncMethod_RequestStream< - EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > - service; + typedef EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>> + SType; + SType service; duplicate::EchoTestService::AsyncService dup_service; SetUpServer(&service, &dup_service, nullptr); ResetStub(); - std::thread response_stream_handler_thread( - [this, &service] { HandleServerStreaming(&service, cqs_[0].get()); }); - std::thread request_stream_handler_thread( - [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + std::thread response_stream_handler_thread(HandleServerStreaming<SType>, + &service, cqs_[0].get()); + std::thread request_stream_handler_thread(HandleClientStreaming<SType>, + &service, cqs_[1].get()); std::thread echo_handler_thread( - [this, &dup_service] { HandleEcho(&dup_service, cqs_[2].get(), true); }); + HandleEcho<duplicate::EchoTestService::AsyncService>, &dup_service, + cqs_[2].get(), true); TestAllMethods(); SendEchoToDupService(); response_stream_handler_thread.join(); @@ -437,25 +443,24 @@ TEST_F(HybridEnd2endTest, GenericEcho) { AsyncGenericService generic_service; SetUpServer(&service, nullptr, &generic_service); ResetStub(); - std::thread generic_handler_thread([this, &generic_service] { - HandleGenericCall(&generic_service, cqs_[0].get()); - }); + std::thread generic_handler_thread(HandleGenericCall, &generic_service, + cqs_[0].get()); TestAllMethods(); generic_handler_thread.join(); } TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStream) { - EchoTestService::WithAsyncMethod_RequestStream< - EchoTestService::WithGenericMethod_Echo<TestServiceImpl> > - service; + typedef EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithGenericMethod_Echo<TestServiceImpl>> + SType; + SType service; AsyncGenericService generic_service; SetUpServer(&service, nullptr, &generic_service); ResetStub(); - std::thread generic_handler_thread([this, &generic_service] { - HandleGenericCall(&generic_service, cqs_[0].get()); - }); - std::thread request_stream_handler_thread( - [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + std::thread generic_handler_thread(HandleGenericCall, &generic_service, + cqs_[0].get()); + std::thread request_stream_handler_thread(HandleClientStreaming<SType>, + &service, cqs_[1].get()); TestAllMethods(); generic_handler_thread.join(); request_stream_handler_thread.join(); @@ -463,18 +468,18 @@ TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStream) { // Add a second service with one sync method. TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStream_SyncDupService) { - EchoTestService::WithAsyncMethod_RequestStream< - EchoTestService::WithGenericMethod_Echo<TestServiceImpl> > - service; + typedef EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithGenericMethod_Echo<TestServiceImpl>> + SType; + SType service; AsyncGenericService generic_service; TestServiceImplDupPkg dup_service; SetUpServer(&service, &dup_service, &generic_service); ResetStub(); - std::thread generic_handler_thread([this, &generic_service] { - HandleGenericCall(&generic_service, cqs_[0].get()); - }); - std::thread request_stream_handler_thread( - [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + std::thread generic_handler_thread(HandleGenericCall, &generic_service, + cqs_[0].get()); + std::thread request_stream_handler_thread(HandleClientStreaming<SType>, + &service, cqs_[1].get()); TestAllMethods(); SendEchoToDupService(); generic_handler_thread.join(); @@ -483,20 +488,21 @@ TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStream_SyncDupService) { // Add a second service with one async method. TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStream_AsyncDupService) { - EchoTestService::WithAsyncMethod_RequestStream< - EchoTestService::WithGenericMethod_Echo<TestServiceImpl> > - service; + typedef EchoTestService::WithAsyncMethod_RequestStream< + EchoTestService::WithGenericMethod_Echo<TestServiceImpl>> + SType; + SType service; AsyncGenericService generic_service; duplicate::EchoTestService::AsyncService dup_service; SetUpServer(&service, &dup_service, &generic_service); ResetStub(); - std::thread generic_handler_thread([this, &generic_service] { - HandleGenericCall(&generic_service, cqs_[0].get()); - }); - std::thread request_stream_handler_thread( - [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); + std::thread generic_handler_thread(HandleGenericCall, &generic_service, + cqs_[0].get()); + std::thread request_stream_handler_thread(HandleClientStreaming<SType>, + &service, cqs_[1].get()); std::thread echo_handler_thread( - [this, &dup_service] { HandleEcho(&dup_service, cqs_[2].get(), true); }); + HandleEcho<duplicate::EchoTestService::AsyncService>, &dup_service, + cqs_[2].get(), true); TestAllMethods(); SendEchoToDupService(); generic_handler_thread.join(); @@ -505,20 +511,20 @@ TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStream_AsyncDupService) { } TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStreamResponseStream) { - EchoTestService::WithAsyncMethod_RequestStream< + typedef EchoTestService::WithAsyncMethod_RequestStream< EchoTestService::WithGenericMethod_Echo< - EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > > - service; + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>>> + SType; + SType service; AsyncGenericService generic_service; SetUpServer(&service, nullptr, &generic_service); ResetStub(); - std::thread generic_handler_thread([this, &generic_service] { - HandleGenericCall(&generic_service, cqs_[0].get()); - }); - std::thread request_stream_handler_thread( - [this, &service] { HandleClientStreaming(&service, cqs_[1].get()); }); - std::thread response_stream_handler_thread( - [this, &service] { HandleServerStreaming(&service, cqs_[2].get()); }); + std::thread generic_handler_thread(HandleGenericCall, &generic_service, + cqs_[0].get()); + std::thread request_stream_handler_thread(HandleClientStreaming<SType>, + &service, cqs_[1].get()); + std::thread response_stream_handler_thread(HandleServerStreaming<SType>, + &service, cqs_[2].get()); TestAllMethods(); generic_handler_thread.join(); request_stream_handler_thread.join(); @@ -526,21 +532,20 @@ TEST_F(HybridEnd2endTest, GenericEchoAsyncRequestStreamResponseStream) { } TEST_F(HybridEnd2endTest, GenericEchoRequestStreamAsyncResponseStream) { - EchoTestService::WithGenericMethod_RequestStream< + typedef EchoTestService::WithGenericMethod_RequestStream< EchoTestService::WithGenericMethod_Echo< - EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > > - service; + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>>> + SType; + SType service; AsyncGenericService generic_service; SetUpServer(&service, nullptr, &generic_service); ResetStub(); - std::thread generic_handler_thread([this, &generic_service] { - HandleGenericCall(&generic_service, cqs_[0].get()); - }); - std::thread generic_handler_thread2([this, &generic_service] { - HandleGenericCall(&generic_service, cqs_[1].get()); - }); - std::thread response_stream_handler_thread( - [this, &service] { HandleServerStreaming(&service, cqs_[2].get()); }); + std::thread generic_handler_thread(HandleGenericCall, &generic_service, + cqs_[0].get()); + std::thread generic_handler_thread2(HandleGenericCall, &generic_service, + cqs_[1].get()); + std::thread response_stream_handler_thread(HandleServerStreaming<SType>, + &service, cqs_[2].get()); TestAllMethods(); generic_handler_thread.join(); generic_handler_thread2.join(); @@ -552,7 +557,7 @@ TEST_F(HybridEnd2endTest, GenericEchoRequestStreamAsyncResponseStream) { TEST_F(HybridEnd2endTest, GenericMethodWithoutGenericService) { EchoTestService::WithGenericMethod_RequestStream< EchoTestService::WithGenericMethod_Echo< - EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl> > > + EchoTestService::WithAsyncMethod_ResponseStream<TestServiceImpl>>> service; SetUpServer(&service, nullptr, nullptr); EXPECT_EQ(nullptr, server_.get()); diff --git a/test/cpp/end2end/proto_server_reflection_test.cc b/test/cpp/end2end/proto_server_reflection_test.cc new file mode 100644 index 0000000000..f8fc39b553 --- /dev/null +++ b/test/cpp/end2end/proto_server_reflection_test.cc @@ -0,0 +1,166 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <google/protobuf/descriptor.h> +#include <grpc++/channel.h> +#include <grpc++/client_context.h> +#include <grpc++/create_channel.h> +#include <grpc++/ext/proto_server_reflection_plugin.h> +#include <grpc++/security/credentials.h> +#include <grpc++/security/server_credentials.h> +#include <grpc++/server.h> +#include <grpc++/server_builder.h> +#include <grpc++/server_context.h> +#include <grpc/grpc.h> +#include <gtest/gtest.h> + +#include "src/proto/grpc/testing/echo.grpc.pb.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" +#include "test/cpp/end2end/test_service_impl.h" +#include "test/cpp/util/proto_reflection_descriptor_database.h" + +namespace grpc { +namespace testing { + +class ProtoServerReflectionTest : public ::testing::Test { + public: + ProtoServerReflectionTest() {} + + void SetUp() GRPC_OVERRIDE { + port_ = grpc_pick_unused_port_or_die(); + ref_desc_pool_ = google::protobuf::DescriptorPool::generated_pool(); + + ServerBuilder builder; + grpc::string server_address = "localhost:" + to_string(port_); + builder.AddListeningPort(server_address, InsecureServerCredentials()); + server_ = builder.BuildAndStart(); + } + + void ResetStub() { + string target = "dns:localhost:" + to_string(port_); + std::shared_ptr<Channel> channel = + CreateChannel(target, InsecureChannelCredentials()); + stub_ = grpc::testing::EchoTestService::NewStub(channel); + desc_db_.reset(new ProtoReflectionDescriptorDatabase(channel)); + desc_pool_.reset(new google::protobuf::DescriptorPool(desc_db_.get())); + } + + string to_string(const int number) { + std::stringstream strs; + strs << number; + return strs.str(); + } + + void CompareService(const grpc::string& service) { + const google::protobuf::ServiceDescriptor* service_desc = + desc_pool_->FindServiceByName(service); + const google::protobuf::ServiceDescriptor* ref_service_desc = + ref_desc_pool_->FindServiceByName(service); + EXPECT_TRUE(service_desc != nullptr); + EXPECT_TRUE(ref_service_desc != nullptr); + EXPECT_EQ(service_desc->DebugString(), ref_service_desc->DebugString()); + + const google::protobuf::FileDescriptor* file_desc = service_desc->file(); + if (known_files_.find(file_desc->package() + "/" + file_desc->name()) != + known_files_.end()) { + EXPECT_EQ(file_desc->DebugString(), + ref_service_desc->file()->DebugString()); + known_files_.insert(file_desc->package() + "/" + file_desc->name()); + } + + for (int i = 0; i < service_desc->method_count(); ++i) { + CompareMethod(service_desc->method(i)->full_name()); + } + } + + void CompareMethod(const grpc::string& method) { + const google::protobuf::MethodDescriptor* method_desc = + desc_pool_->FindMethodByName(method); + const google::protobuf::MethodDescriptor* ref_method_desc = + ref_desc_pool_->FindMethodByName(method); + EXPECT_TRUE(method_desc != nullptr); + EXPECT_TRUE(ref_method_desc != nullptr); + EXPECT_EQ(method_desc->DebugString(), ref_method_desc->DebugString()); + + CompareType(method_desc->input_type()->full_name()); + CompareType(method_desc->output_type()->full_name()); + } + + void CompareType(const grpc::string& type) { + if (known_types_.find(type) != known_types_.end()) { + return; + } + + const google::protobuf::Descriptor* desc = + desc_pool_->FindMessageTypeByName(type); + const google::protobuf::Descriptor* ref_desc = + ref_desc_pool_->FindMessageTypeByName(type); + EXPECT_TRUE(desc != nullptr); + EXPECT_TRUE(ref_desc != nullptr); + EXPECT_EQ(desc->DebugString(), ref_desc->DebugString()); + } + + protected: + std::unique_ptr<Server> server_; + std::unique_ptr<grpc::testing::EchoTestService::Stub> stub_; + std::unique_ptr<ProtoReflectionDescriptorDatabase> desc_db_; + std::unique_ptr<google::protobuf::DescriptorPool> desc_pool_; + std::unordered_set<string> known_files_; + std::unordered_set<string> known_types_; + const google::protobuf::DescriptorPool* ref_desc_pool_; + int port_; + reflection::ProtoServerReflectionPlugin plugin_; +}; + +TEST_F(ProtoServerReflectionTest, CheckResponseWithLocalDescriptorPool) { + ResetStub(); + + std::vector<std::string> services; + desc_db_->GetServices(&services); + // The service list has at least one service (reflection servcie). + EXPECT_TRUE(services.size() > 0); + + for (auto it = services.begin(); it != services.end(); ++it) { + CompareService(*it); + } +} + +} // 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 8a74621e5a..778a2be573 100644 --- a/test/cpp/end2end/server_builder_plugin_test.cc +++ b/test/cpp/end2end/server_builder_plugin_test.cc @@ -37,6 +37,7 @@ #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> @@ -61,6 +62,7 @@ class TestServerBuilderPlugin : public ServerBuilderPlugin { init_server_is_called_ = false; finish_is_called_ = false; change_arguments_is_called_ = false; + register_service_ = false; } grpc::string name() GRPC_OVERRIDE { return PLUGIN_NAME; } @@ -112,15 +114,14 @@ class InsertPluginServerBuilderOption : public ServerBuilderOption { void UpdateArguments(ChannelArguments* arg) GRPC_OVERRIDE {} - void UpdatePlugins( - std::map<grpc::string, std::unique_ptr<ServerBuilderPlugin>>* plugins) + void UpdatePlugins(std::vector<std::unique_ptr<ServerBuilderPlugin>>* plugins) GRPC_OVERRIDE { plugins->clear(); std::unique_ptr<TestServerBuilderPlugin> plugin( new TestServerBuilderPlugin()); if (register_service_) plugin->SetRegisterService(); - (*plugins)[plugin->name()] = std::move(plugin); + plugins->emplace_back(std::move(plugin)); } void SetRegisterService() { register_service_ = true; } @@ -161,7 +162,7 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> { void InsertPlugin() { if (GetParam()) { // Add ServerBuilder plugin in static initialization - EXPECT_TRUE(builder_->plugins_[PLUGIN_NAME] != nullptr); + CheckPresent(); } else { // Add ServerBuilder plugin using ServerBuilder::SetOption() builder_->SetOption(std::unique_ptr<ServerBuilderOption>( @@ -172,10 +173,8 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> { void InsertPluginWithTestService() { if (GetParam()) { // Add ServerBuilder plugin in static initialization - EXPECT_TRUE(builder_->plugins_[PLUGIN_NAME] != nullptr); - auto plugin = static_cast<TestServerBuilderPlugin*>( - builder_->plugins_[PLUGIN_NAME].get()); - EXPECT_TRUE(plugin != nullptr); + auto plugin = CheckPresent(); + EXPECT_TRUE(plugin); plugin->SetRegisterService(); } else { // Add ServerBuilder plugin using ServerBuilder::SetOption() @@ -189,9 +188,12 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> { void StartServer() { grpc::string server_address = "localhost:" + to_string(port_); builder_->AddListeningPort(server_address, InsecureServerCredentials()); + // we run some tests without a service, and for those we need to supply a + // frequently polled completion queue cq_ = builder_->AddCompletionQueue(); + cq_thread_ = grpc::thread(std::bind(&ServerBuilderPluginTest::RunCQ, this)); server_ = builder_->BuildAndStart(); - EXPECT_TRUE(builder_->plugins_[PLUGIN_NAME] != nullptr); + EXPECT_TRUE(CheckPresent()); } void ResetStub() { @@ -201,18 +203,13 @@ class ServerBuilderPluginTest : public ::testing::TestWithParam<bool> { } void TearDown() GRPC_OVERRIDE { - EXPECT_TRUE(builder_->plugins_[PLUGIN_NAME] != nullptr); - auto plugin = static_cast<TestServerBuilderPlugin*>( - builder_->plugins_[PLUGIN_NAME].get()); - EXPECT_TRUE(plugin != nullptr); + auto plugin = CheckPresent(); + EXPECT_TRUE(plugin); EXPECT_TRUE(plugin->init_server_is_called()); EXPECT_TRUE(plugin->finish_is_called()); server_->Shutdown(); - void* tag; - bool ok; cq_->Shutdown(); - while (cq_->Next(&tag, &ok)) - ; + cq_thread_.join(); } string to_string(const int number) { @@ -227,8 +224,29 @@ 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_; TestServiceImpl service_; int port_; + + private: + TestServerBuilderPlugin* CheckPresent() { + auto it = builder_->plugins_.begin(); + for (; it != builder_->plugins_.end(); it++) { + if ((*it)->name() == PLUGIN_NAME) break; + } + if (it != builder_->plugins_.end()) { + return static_cast<TestServerBuilderPlugin*>(it->get()); + } else { + return nullptr; + } + } + + void RunCQ() { + void* tag; + bool ok; + while (cq_->Next(&tag, &ok)) + ; + } }; TEST_P(ServerBuilderPluginTest, PluginWithoutServiceTest) { diff --git a/test/cpp/end2end/test_service_impl.cc b/test/cpp/end2end/test_service_impl.cc index cbaee92228..52abd80d69 100644 --- a/test/cpp/end2end/test_service_impl.cc +++ b/test/cpp/end2end/test_service_impl.cc @@ -33,6 +33,7 @@ #include "test/cpp/end2end/test_service_impl.h" +#include <string> #include <thread> #include <grpc++/security/credentials.h> @@ -253,7 +254,7 @@ Status TestServiceImpl::ResponseStream(ServerContext* context, } for (int i = 0; i < kNumResponseStreamsMsgs; i++) { - response.set_message(request->message() + std::to_string(i)); + response.set_message(request->message() + grpc::to_string(i)); writer->Write(response); } diff --git a/test/cpp/end2end/thread_stress_test.cc b/test/cpp/end2end/thread_stress_test.cc index 94541f9a45..b021b34523 100644 --- a/test/cpp/end2end/thread_stress_test.cc +++ b/test/cpp/end2end/thread_stress_test.cc @@ -230,7 +230,7 @@ class CommonStressTestSyncServer : public CommonStressTest<TestServiceImpl> { }; class CommonStressTestAsyncServer - : public CommonStressTest<::grpc::testing::EchoTestService::AsyncService> { + : public CommonStressTest<grpc::testing::EchoTestService::AsyncService> { public: void SetUp() GRPC_OVERRIDE { shutting_down_ = false; @@ -394,7 +394,7 @@ class AsyncClientEnd2endTest : public ::testing::Test { for (int i = 0; i < num_rpcs; ++i) { AsyncClientCall* call = new AsyncClientCall; EchoRequest request; - request.set_message("Hello: " + std::to_string(i)); + request.set_message("Hello: " + grpc::to_string(i)); call->response_reader = common_.GetStub()->AsyncEcho(&call->context, request, &cq_); call->response_reader->Finish(&call->response, &call->status, diff --git a/test/cpp/end2end/zookeeper_test.cc b/test/cpp/end2end/zookeeper_test.cc index 12853a1b98..fdc500e535 100644 --- a/test/cpp/end2end/zookeeper_test.cc +++ b/test/cpp/end2end/zookeeper_test.cc @@ -101,7 +101,7 @@ class ZookeeperTest : public ::testing::Test { zookeeper_address_ = addr_str; gpr_free(addr); } - gpr_log(GPR_DEBUG, zookeeper_address_.c_str()); + gpr_log(GPR_DEBUG, "%s", zookeeper_address_.c_str()); // Connects to zookeeper server zoo_set_debug_level(ZOO_LOG_LEVEL_WARN); diff --git a/test/cpp/interop/client.cc b/test/cpp/interop/client.cc index 7727824979..e8ae6ee572 100644 --- a/test/cpp/interop/client.cc +++ b/test/cpp/interop/client.cc @@ -40,7 +40,9 @@ #include <grpc++/client_context.h> #include <grpc/grpc.h> #include <grpc/support/log.h> +#include <grpc/support/useful.h> +#include "src/core/lib/support/string.h" #include "test/cpp/interop/client_helper.h" #include "test/cpp/interop/interop_client.h" #include "test/cpp/util/test_config.h" @@ -52,30 +54,31 @@ DEFINE_string(server_host, "127.0.0.1", "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: " - "empty_unary : empty (zero bytes) request and response; " - "large_unary : single request and (large) response; " - "large_compressed_unary : single request and compressed (large) " - "response; " - "client_streaming : request streaming with single response; " - "server_streaming : single request with response streaming; " + "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 " - "response streaming; " - "slow_consumer : single request with response; " - " streaming with slow client consumer; " - "half_duplex : half-duplex streaming; " - "ping_pong : full-duplex streaming; " - "cancel_after_begin : cancel stream after starting it; " - "cancel_after_first_response: cancel on first response; " - "timeout_on_sleeping_server: deadline exceeds on stream; " - "empty_stream : bi-di stream with no request/response; " - "compute_engine_creds: large_unary with compute engine auth; " - "jwt_token_creds: large_unary with JWT token auth; " - "oauth2_auth_token: raw oauth2 access token auth; " - "per_rpc_creds: raw oauth2 access token on a single rpc; " - "status_code_and_message: verify status code & message; " - "custom_metadata: server will echo custom metadata;" - "all : all of above."); + "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"); DEFINE_string(default_service_account, "", "Email of GCE default service account"); DEFINE_string(service_account_key_file, "", @@ -104,14 +107,18 @@ int main(int argc, char** argv) { client.DoEmpty(); } else if (FLAGS_test_case == "large_unary") { client.DoLargeUnary(); - } else if (FLAGS_test_case == "large_compressed_unary") { - client.DoLargeCompressedUnary(); + } 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.DoResponseCompressedStreaming(); + 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") { @@ -144,9 +151,12 @@ int main(int argc, char** argv) { } else if (FLAGS_test_case == "all") { client.DoEmpty(); client.DoLargeUnary(); + client.DoClientCompressedUnary(); + client.DoServerCompressedUnary(); client.DoRequestStreaming(); client.DoResponseStreaming(); - client.DoResponseCompressedStreaming(); + client.DoClientCompressedStreaming(); + client.DoServerCompressedStreaming(); client.DoHalfDuplex(); client.DoPingPong(); client.DoCancelAfterBegin(); @@ -165,14 +175,35 @@ int main(int argc, char** argv) { } // compute_engine_creds only runs in GCE. } else { - gpr_log( - GPR_ERROR, - "Unsupported test case %s. Valid options are all|empty_unary|" - "large_unary|large_compressed_unary|client_streaming|server_streaming|" - "server_compressed_streaming|half_duplex|ping_pong|cancel_after_begin|" - "cancel_after_first_response|timeout_on_sleeping_server|empty_stream|" - "compute_engine_creds|jwt_token_creds|oauth2_auth_token|per_rpc_creds", - "status_code_and_message|custom_metadata", FLAGS_test_case.c_str()); + const char* testcases[] = {"all", + "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"}; + 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; } diff --git a/test/cpp/interop/interop_client.cc b/test/cpp/interop/interop_client.cc index 189e4a8aab..8861bc1163 100644 --- a/test/cpp/interop/interop_client.cc +++ b/test/cpp/interop/interop_client.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,10 +31,8 @@ * */ -#include "test/cpp/interop/interop_client.h" - #include <unistd.h> - +#include <cinttypes> #include <fstream> #include <memory> @@ -51,51 +49,41 @@ #include "src/proto/grpc/testing/messages.grpc.pb.h" #include "src/proto/grpc/testing/test.grpc.pb.h" #include "test/cpp/interop/client_helper.h" +#include "test/cpp/interop/interop_client.h" namespace grpc { namespace testing { -static const char* kRandomFile = "test/cpp/interop/rnd.dat"; - namespace { // The same value is defined by the Java client. const std::vector<int> request_stream_sizes = {27182, 8, 1828, 45904}; -const std::vector<int> response_stream_sizes = {31415, 59, 2653, 58979}; +const std::vector<int> response_stream_sizes = {31415, 9, 2653, 58979}; const int kNumResponseMessages = 2000; const int kResponseMessageSize = 1030; const int kReceiveDelayMilliSeconds = 20; const int kLargeRequestSize = 271828; const int kLargeResponseSize = 314159; -CompressionType GetInteropCompressionTypeFromCompressionAlgorithm( - grpc_compression_algorithm algorithm) { - switch (algorithm) { - case GRPC_COMPRESS_NONE: - return CompressionType::NONE; - case GRPC_COMPRESS_GZIP: - return CompressionType::GZIP; - case GRPC_COMPRESS_DEFLATE: - return CompressionType::DEFLATE; - default: - GPR_ASSERT(false); - } -} - void NoopChecks(const InteropClientContextInspector& inspector, const SimpleRequest* request, const SimpleResponse* response) {} -void CompressionChecks(const InteropClientContextInspector& inspector, - const SimpleRequest* request, - const SimpleResponse* response) { - GPR_ASSERT(request->response_compression() == - GetInteropCompressionTypeFromCompressionAlgorithm( - inspector.GetCallCompressionAlgorithm())); - if (request->response_compression() == NONE) { - GPR_ASSERT(!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS)); - } else if (request->response_type() == PayloadType::COMPRESSABLE) { - // requested compression and compressable response => results should always - // be compressed. +void UnaryCompressionChecks(const InteropClientContextInspector& inspector, + const SimpleRequest* request, + const SimpleResponse* response) { + const grpc_compression_algorithm received_compression = + inspector.GetCallCompressionAlgorithm(); + if (request->response_compressed().value()) { + if (received_compression == GRPC_COMPRESS_NONE) { + // Requested some compression, got NONE. This is an error. + gpr_log(GPR_ERROR, + "Failure: Requested compression but got uncompressed response " + "from server."); + abort(); + } GPR_ASSERT(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS); + } else { + // Didn't request compression -> make sure the response is uncompressed + GPR_ASSERT(!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS)); } } } // namespace @@ -197,11 +185,16 @@ bool InteropClient::PerformLargeUnary(SimpleRequest* request, CheckerFn custom_checks_fn) { ClientContext context; InteropClientContextInspector inspector(context); - // If the request doesn't already specify the response type, default to - // COMPRESSABLE. request->set_response_size(kLargeResponseSize); grpc::string payload(kLargeRequestSize, '\0'); request->mutable_payload()->set_body(payload.c_str(), kLargeRequestSize); + if (request->has_expect_compressed()) { + if (request->expect_compressed().value()) { + context.set_compression_algorithm(GRPC_COMPRESS_GZIP); + } else { + context.set_compression_algorithm(GRPC_COMPRESS_NONE); + } + } Status s = serviceStub_.Get()->UnaryCall(&context, *request, response); if (!AssertStatusOk(s)) { @@ -211,25 +204,8 @@ bool InteropClient::PerformLargeUnary(SimpleRequest* request, custom_checks_fn(inspector, request, response); // Payload related checks. - if (request->response_type() != PayloadType::RANDOM) { - GPR_ASSERT(response->payload().type() == request->response_type()); - } - switch (response->payload().type()) { - case PayloadType::COMPRESSABLE: - GPR_ASSERT(response->payload().body() == - grpc::string(kLargeResponseSize, '\0')); - break; - case PayloadType::UNCOMPRESSABLE: { - std::ifstream rnd_file(kRandomFile); - GPR_ASSERT(rnd_file.good()); - for (int i = 0; i < kLargeResponseSize; i++) { - GPR_ASSERT(response->payload().body()[i] == (char)rnd_file.get()); - } - } break; - default: - GPR_ASSERT(false); - } - + GPR_ASSERT(response->payload().body() == + grpc::string(kLargeResponseSize, '\0')); return true; } @@ -242,7 +218,6 @@ bool InteropClient::DoComputeEngineCreds( SimpleResponse response; request.set_fill_username(true); request.set_fill_oauth_scope(true); - request.set_response_type(PayloadType::COMPRESSABLE); if (!PerformLargeUnary(&request, &response)) { return false; @@ -316,7 +291,6 @@ bool InteropClient::DoJwtTokenCreds(const grpc::string& username) { SimpleRequest request; SimpleResponse response; request.set_fill_username(true); - request.set_response_type(PayloadType::COMPRESSABLE); if (!PerformLargeUnary(&request, &response)) { return false; @@ -332,7 +306,6 @@ bool InteropClient::DoLargeUnary() { gpr_log(GPR_DEBUG, "Sending a large unary rpc..."); SimpleRequest request; SimpleResponse response; - request.set_response_type(PayloadType::COMPRESSABLE); if (!PerformLargeUnary(&request, &response)) { return false; } @@ -340,32 +313,73 @@ bool InteropClient::DoLargeUnary() { return true; } -bool InteropClient::DoLargeCompressedUnary() { - const CompressionType compression_types[] = {NONE, GZIP, DEFLATE}; - const PayloadType payload_types[] = {COMPRESSABLE, UNCOMPRESSABLE, RANDOM}; - for (size_t i = 0; i < GPR_ARRAY_SIZE(payload_types); i++) { - for (size_t j = 0; j < GPR_ARRAY_SIZE(compression_types); j++) { - char* log_suffix; - gpr_asprintf(&log_suffix, "(compression=%s; payload=%s)", - CompressionType_Name(compression_types[j]).c_str(), - PayloadType_Name(payload_types[i]).c_str()); - - gpr_log(GPR_DEBUG, "Sending a large compressed unary rpc %s.", - log_suffix); - SimpleRequest request; - SimpleResponse response; - request.set_response_type(payload_types[i]); - request.set_response_compression(compression_types[j]); - - if (!PerformLargeUnary(&request, &response, CompressionChecks)) { - gpr_log(GPR_ERROR, "Large compressed unary failed %s", log_suffix); - gpr_free(log_suffix); - return false; - } - - gpr_log(GPR_DEBUG, "Large compressed unary done %s.", log_suffix); +bool InteropClient::DoClientCompressedUnary() { + // Probing for compression-checks support. + ClientContext probe_context; + SimpleRequest probe_req; + SimpleResponse probe_res; + + probe_context.set_compression_algorithm(GRPC_COMPRESS_NONE); + probe_req.mutable_expect_compressed()->set_value(true); // lies! + + probe_req.set_response_size(kLargeResponseSize); + probe_req.mutable_payload()->set_body(grpc::string(kLargeRequestSize, '\0')); + + gpr_log(GPR_DEBUG, "Sending probe for compressed unary request."); + const Status s = + serviceStub_.Get()->UnaryCall(&probe_context, probe_req, &probe_res); + if (s.error_code() != grpc::StatusCode::INVALID_ARGUMENT) { + // The server isn't able to evaluate incoming compression, making the rest + // of this test moot. + gpr_log(GPR_DEBUG, "Compressed unary request probe failed"); + return false; + } + gpr_log(GPR_DEBUG, "Compressed unary request probe succeeded. Proceeding."); + + const std::vector<bool> compressions = {true, false}; + for (size_t i = 0; i < compressions.size(); i++) { + char* log_suffix; + gpr_asprintf(&log_suffix, "(compression=%s)", + compressions[i] ? "true" : "false"); + + gpr_log(GPR_DEBUG, "Sending compressed unary request %s.", log_suffix); + SimpleRequest request; + SimpleResponse response; + request.mutable_expect_compressed()->set_value(compressions[i]); + if (!PerformLargeUnary(&request, &response, UnaryCompressionChecks)) { + gpr_log(GPR_ERROR, "Compressed unary request failed %s", log_suffix); gpr_free(log_suffix); + return false; } + + gpr_log(GPR_DEBUG, "Compressed unary request failed %s", log_suffix); + gpr_free(log_suffix); + } + + return true; +} + +bool InteropClient::DoServerCompressedUnary() { + const std::vector<bool> compressions = {true, false}; + for (size_t i = 0; i < compressions.size(); i++) { + char* log_suffix; + gpr_asprintf(&log_suffix, "(compression=%s)", + compressions[i] ? "true" : "false"); + + gpr_log(GPR_DEBUG, "Sending unary request for compressed response %s.", + log_suffix); + SimpleRequest request; + SimpleResponse response; + request.mutable_response_compressed()->set_value(compressions[i]); + + if (!PerformLargeUnary(&request, &response, UnaryCompressionChecks)) { + gpr_log(GPR_ERROR, "Request for compressed unary failed %s", log_suffix); + gpr_free(log_suffix); + return false; + } + + gpr_log(GPR_DEBUG, "Request for compressed unary failed %s", log_suffix); + gpr_free(log_suffix); } return true; @@ -392,7 +406,7 @@ bool InteropClient::DoRequestStreaming() { serviceStub_.Get()->StreamingInputCall(&context, &response)); int aggregated_payload_size = 0; - for (unsigned int i = 0; i < request_stream_sizes.size(); ++i) { + for (size_t i = 0; i < request_stream_sizes.size(); ++i) { Payload* payload = request.mutable_payload(); payload->set_body(grpc::string(request_stream_sizes[i], '\0')); if (!stream->Write(request)) { @@ -401,7 +415,7 @@ bool InteropClient::DoRequestStreaming() { } aggregated_payload_size += request_stream_sizes[i]; } - stream->WritesDone(); + GPR_ASSERT(stream->WritesDone()); Status s = stream->Finish(); if (!AssertStatusOk(s)) { @@ -437,7 +451,7 @@ bool InteropClient::DoResponseStreaming() { // most likely due to connection failure. gpr_log(GPR_ERROR, "DoResponseStreaming(): Read fewer streams (%d) than " - "response_stream_sizes.size() (%d)", + "response_stream_sizes.size() (%" PRIuPTR ")", i, response_stream_sizes.size()); return TransientFailureOrAbort(); } @@ -451,95 +465,128 @@ bool InteropClient::DoResponseStreaming() { return true; } -bool InteropClient::DoResponseCompressedStreaming() { - const CompressionType compression_types[] = {NONE, GZIP, DEFLATE}; - const PayloadType payload_types[] = {COMPRESSABLE, UNCOMPRESSABLE, RANDOM}; - for (size_t i = 0; i < GPR_ARRAY_SIZE(payload_types); i++) { - for (size_t j = 0; j < GPR_ARRAY_SIZE(compression_types); j++) { - ClientContext context; - InteropClientContextInspector inspector(context); - StreamingOutputCallRequest request; - - char* log_suffix; - gpr_asprintf(&log_suffix, "(compression=%s; payload=%s)", - CompressionType_Name(compression_types[j]).c_str(), - PayloadType_Name(payload_types[i]).c_str()); - - gpr_log(GPR_DEBUG, "Receiving response streaming rpc %s.", log_suffix); - - request.set_response_type(payload_types[i]); - request.set_response_compression(compression_types[j]); - - for (size_t k = 0; k < response_stream_sizes.size(); ++k) { - ResponseParameters* response_parameter = - request.add_response_parameters(); - response_parameter->set_size(response_stream_sizes[k]); - } - StreamingOutputCallResponse response; - - std::unique_ptr<ClientReader<StreamingOutputCallResponse>> stream( - serviceStub_.Get()->StreamingOutputCall(&context, request)); - - size_t k = 0; - while (stream->Read(&response)) { - // Payload related checks. - if (request.response_type() != PayloadType::RANDOM) { - GPR_ASSERT(response.payload().type() == request.response_type()); - } - switch (response.payload().type()) { - case PayloadType::COMPRESSABLE: - GPR_ASSERT(response.payload().body() == - grpc::string(response_stream_sizes[k], '\0')); - break; - case PayloadType::UNCOMPRESSABLE: { - std::ifstream rnd_file(kRandomFile); - GPR_ASSERT(rnd_file.good()); - for (int n = 0; n < response_stream_sizes[k]; n++) { - GPR_ASSERT(response.payload().body()[n] == (char)rnd_file.get()); - } - } break; - default: - GPR_ASSERT(false); - } - - // Compression related checks. - GPR_ASSERT(request.response_compression() == - GetInteropCompressionTypeFromCompressionAlgorithm( - inspector.GetCallCompressionAlgorithm())); - if (request.response_compression() == NONE) { - GPR_ASSERT( - !(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS)); - } else if (request.response_type() == PayloadType::COMPRESSABLE) { - // requested compression and compressable response => results should - // always be compressed. - GPR_ASSERT(inspector.GetMessageFlags() & - GRPC_WRITE_INTERNAL_COMPRESS); - } - - ++k; - } - - gpr_log(GPR_DEBUG, "Response streaming done %s.", log_suffix); - gpr_free(log_suffix); +bool InteropClient::DoClientCompressedStreaming() { + // Probing for compression-checks support. + ClientContext probe_context; + StreamingInputCallRequest probe_req; + StreamingInputCallResponse probe_res; + + probe_context.set_compression_algorithm(GRPC_COMPRESS_NONE); + probe_req.mutable_expect_compressed()->set_value(true); // lies! + probe_req.mutable_payload()->set_body(grpc::string(27182, '\0')); + + gpr_log(GPR_DEBUG, "Sending probe for compressed streaming request."); - if (k < response_stream_sizes.size()) { - // stream->Read() failed before reading all the expected messages. This - // is most likely due to a connection failure. - gpr_log(GPR_ERROR, - "DoResponseCompressedStreaming(): Responses read (k=%d) is " - "less than the expected messages (i.e " - "response_stream_sizes.size() (%d)). (i=%d, j=%d)", - k, response_stream_sizes.size(), i, j); - return TransientFailureOrAbort(); - } - - Status s = stream->Finish(); - if (!AssertStatusOk(s)) { - return false; - } + std::unique_ptr<ClientWriter<StreamingInputCallRequest>> probe_stream( + serviceStub_.Get()->StreamingInputCall(&probe_context, &probe_res)); + + if (!probe_stream->Write(probe_req)) { + gpr_log(GPR_ERROR, "%s(): stream->Write() failed", __func__); + return TransientFailureOrAbort(); + } + Status s = probe_stream->Finish(); + if (s.error_code() != grpc::StatusCode::INVALID_ARGUMENT) { + // The server isn't able to evaluate incoming compression, making the rest + // of this test moot. + gpr_log(GPR_DEBUG, "Compressed streaming request probe failed"); + return false; + } + gpr_log(GPR_DEBUG, + "Compressed streaming request probe succeeded. Proceeding."); + + ClientContext context; + StreamingInputCallRequest request; + StreamingInputCallResponse response; + + context.set_compression_algorithm(GRPC_COMPRESS_GZIP); + std::unique_ptr<ClientWriter<StreamingInputCallRequest>> stream( + serviceStub_.Get()->StreamingInputCall(&context, &response)); + + request.mutable_payload()->set_body(grpc::string(27182, '\0')); + request.mutable_expect_compressed()->set_value(true); + gpr_log(GPR_DEBUG, "Sending streaming request with compression enabled"); + if (!stream->Write(request)) { + gpr_log(GPR_ERROR, "%s(): stream->Write() failed", __func__); + return TransientFailureOrAbort(); + } + + WriteOptions wopts; + wopts.set_no_compression(); + request.mutable_payload()->set_body(grpc::string(45904, '\0')); + request.mutable_expect_compressed()->set_value(false); + gpr_log(GPR_DEBUG, "Sending streaming request with compression disabled"); + if (!stream->Write(request, wopts)) { + gpr_log(GPR_ERROR, "%s(): stream->Write() failed", __func__); + return TransientFailureOrAbort(); + } + GPR_ASSERT(stream->WritesDone()); + + s = stream->Finish(); + if (!AssertStatusOk(s)) { + return false; + } + + return true; +} + +bool InteropClient::DoServerCompressedStreaming() { + const std::vector<bool> compressions = {true, false}; + const std::vector<int> sizes = {31415, 92653}; + + ClientContext context; + InteropClientContextInspector inspector(context); + StreamingOutputCallRequest request; + + GPR_ASSERT(compressions.size() == sizes.size()); + for (size_t i = 0; i < sizes.size(); i++) { + char* log_suffix; + gpr_asprintf(&log_suffix, "(compression=%s; size=%d)", + compressions[i] ? "true" : "false", sizes[i]); + + gpr_log(GPR_DEBUG, "Sending request streaming rpc %s.", log_suffix); + gpr_free(log_suffix); + + ResponseParameters* const response_parameter = + request.add_response_parameters(); + response_parameter->mutable_compressed()->set_value(compressions[i]); + response_parameter->set_size(sizes[i]); + } + std::unique_ptr<ClientReader<StreamingOutputCallResponse>> stream( + serviceStub_.Get()->StreamingOutputCall(&context, request)); + + size_t k = 0; + StreamingOutputCallResponse response; + while (stream->Read(&response)) { + // Payload size checks. + GPR_ASSERT(response.payload().body() == + grpc::string(request.response_parameters(k).size(), '\0')); + + // Compression checks. + GPR_ASSERT(request.response_parameters(k).has_compressed()); + if (request.response_parameters(k).compressed().value()) { + GPR_ASSERT(inspector.GetCallCompressionAlgorithm() > GRPC_COMPRESS_NONE); + GPR_ASSERT(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS); + } else { + // requested *no* compression. + GPR_ASSERT(!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS)); } + ++k; } + if (k < sizes.size()) { + // stream->Read() failed before reading all the expected messages. This + // is most likely due to a connection failure. + gpr_log(GPR_ERROR, + "%s(): Responses read (k=%" PRIuPTR + ") is less than the expected number of messages (%" PRIuPTR ").", + __func__, k, sizes.size()); + return TransientFailureOrAbort(); + } + + Status s = stream->Finish(); + if (!AssertStatusOk(s)) { + return false; + } return true; } @@ -617,7 +664,7 @@ bool InteropClient::DoHalfDuplex() { // most likely due to a connection failure gpr_log(GPR_ERROR, "DoHalfDuplex(): Responses read (i=%d) are less than the expected " - "number of messages response_stream_sizes.size() (%d)", + "number of messages response_stream_sizes.size() (%" PRIuPTR ")", i, response_stream_sizes.size()); return TransientFailureOrAbort(); } @@ -640,7 +687,6 @@ bool InteropClient::DoPingPong() { stream(serviceStub_.Get()->FullDuplexCall(&context)); StreamingOutputCallRequest request; - request.set_response_type(PayloadType::COMPRESSABLE); ResponseParameters* response_parameter = request.add_response_parameters(); Payload* payload = request.mutable_payload(); StreamingOutputCallResponse response; @@ -707,7 +753,6 @@ bool InteropClient::DoCancelAfterFirstResponse() { stream(serviceStub_.Get()->FullDuplexCall(&context)); StreamingOutputCallRequest request; - request.set_response_type(PayloadType::COMPRESSABLE); ResponseParameters* response_parameter = request.add_response_parameters(); response_parameter->set_size(31415); request.mutable_payload()->set_body(grpc::string(27182, '\0')); @@ -847,7 +892,6 @@ bool InteropClient::DoCustomMetadata() { stream(serviceStub_.Get()->FullDuplexCall(&context)); StreamingOutputCallRequest request; - request.set_response_type(PayloadType::COMPRESSABLE); ResponseParameters* response_parameter = request.add_response_parameters(); response_parameter->set_size(kLargeResponseSize); grpc::string payload(kLargeRequestSize, '\0'); diff --git a/test/cpp/interop/interop_client.h b/test/cpp/interop/interop_client.h index ae75762bb8..eb886fcb7e 100644 --- a/test/cpp/interop/interop_client.h +++ b/test/cpp/interop/interop_client.h @@ -45,9 +45,9 @@ namespace grpc { namespace testing { // Function pointer for custom checks. -using CheckerFn = - std::function<void(const InteropClientContextInspector&, - const SimpleRequest*, const SimpleResponse*)>; +typedef std::function<void(const InteropClientContextInspector&, + const SimpleRequest*, const SimpleResponse*)> + CheckerFn; class InteropClient { public: @@ -64,12 +64,14 @@ class InteropClient { bool DoEmpty(); bool DoLargeUnary(); - bool DoLargeCompressedUnary(); + bool DoServerCompressedUnary(); + bool DoClientCompressedUnary(); bool DoPingPong(); bool DoHalfDuplex(); bool DoRequestStreaming(); bool DoResponseStreaming(); - bool DoResponseCompressedStreaming(); + bool DoServerCompressedStreaming(); + bool DoClientCompressedStreaming(); bool DoResponseStreamingWithSlowConsumer(); bool DoCancelAfterBegin(); bool DoCancelAfterFirstResponse(); diff --git a/test/cpp/interop/server_main.cc b/test/cpp/interop/interop_server.cc index 889874fe49..ebef0002a3 100644 --- a/test/cpp/interop/server_main.cc +++ b/test/cpp/interop/interop_server.cc @@ -1,6 +1,6 @@ /* * - * Copyright 2015, Google Inc. + * Copyright 2015-2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,6 +48,7 @@ #include <grpc/support/log.h> #include <grpc/support/useful.h> +#include "src/core/lib/transport/byte_stream.h" #include "src/proto/grpc/testing/empty.grpc.pb.h" #include "src/proto/grpc/testing/messages.grpc.pb.h" #include "src/proto/grpc/testing/test.grpc.pb.h" @@ -64,10 +65,10 @@ using grpc::ServerCredentials; using grpc::ServerReader; using grpc::ServerReaderWriter; using grpc::ServerWriter; +using grpc::WriteOptions; using grpc::SslServerCredentialsOptions; using grpc::testing::InteropServerContextInspector; using grpc::testing::Payload; -using grpc::testing::PayloadType; using grpc::testing::SimpleRequest; using grpc::testing::SimpleResponse; using grpc::testing::StreamingInputCallRequest; @@ -78,7 +79,6 @@ using grpc::testing::TestService; using grpc::Status; static bool got_sigint = false; -static const char* kRandomFile = "test/cpp/interop/rnd.dat"; const char kEchoInitialMetadataKey[] = "x-grpc-test-echo-initial"; const char kEchoTrailingBinMetadataKey[] = "x-grpc-test-echo-trailing-bin"; @@ -110,50 +110,41 @@ void MaybeEchoMetadata(ServerContext* context) { } } -bool SetPayload(PayloadType type, int size, Payload* payload) { - PayloadType response_type; - if (type == PayloadType::RANDOM) { - response_type = - rand() & 0x1 ? PayloadType::COMPRESSABLE : PayloadType::UNCOMPRESSABLE; - } else { - response_type = type; - } - payload->set_type(response_type); - switch (response_type) { - case PayloadType::COMPRESSABLE: { - std::unique_ptr<char[]> body(new char[size]()); - payload->set_body(body.get(), size); - } break; - case PayloadType::UNCOMPRESSABLE: { - std::unique_ptr<char[]> body(new char[size]()); - std::ifstream rnd_file(kRandomFile); - GPR_ASSERT(rnd_file.good()); - rnd_file.read(body.get(), size); - GPR_ASSERT(!rnd_file.eof()); // Requested more rnd bytes than available - payload->set_body(body.get(), size); - } break; - default: - GPR_ASSERT(false); - } +bool SetPayload(int size, Payload* payload) { + std::unique_ptr<char[]> body(new char[size]()); + payload->set_body(body.get(), size); return true; } -template <typename RequestType> -void SetResponseCompression(ServerContext* context, - const RequestType& request) { - switch (request.response_compression()) { - case grpc::testing::NONE: - context->set_compression_algorithm(GRPC_COMPRESS_NONE); - break; - case grpc::testing::GZIP: - context->set_compression_algorithm(GRPC_COMPRESS_GZIP); - break; - case grpc::testing::DEFLATE: - context->set_compression_algorithm(GRPC_COMPRESS_DEFLATE); - break; - default: - abort(); +bool CheckExpectedCompression(const ServerContext& context, + const bool compression_expected) { + const InteropServerContextInspector inspector(context); + const grpc_compression_algorithm received_compression = + inspector.GetCallCompressionAlgorithm(); + + if (compression_expected) { + if (received_compression == GRPC_COMPRESS_NONE) { + // Expected some compression, got NONE. This is an error. + gpr_log(GPR_ERROR, + "Expected compression but got uncompressed request from client."); + return false; + } + if (!(inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS)) { + gpr_log(GPR_ERROR, + "Failure: Requested compression in a compressable request, but " + "compression bit in message flags not set."); + return false; + } + } else { + // Didn't expect compression -> make sure the request is uncompressed + if (inspector.GetMessageFlags() & GRPC_WRITE_INTERNAL_COMPRESS) { + gpr_log(GPR_ERROR, + "Failure: Didn't requested compression, but compression bit in " + "message flags set."); + return false; + } } + return true; } class TestServiceImpl : public TestService::Service { @@ -167,11 +158,26 @@ class TestServiceImpl : public TestService::Service { Status UnaryCall(ServerContext* context, const SimpleRequest* request, SimpleResponse* response) { MaybeEchoMetadata(context); - SetResponseCompression(context, *request); + if (request->has_response_compressed()) { + const bool compression_requested = request->response_compressed().value(); + gpr_log(GPR_DEBUG, "Request for compression (%s) present for %s", + compression_requested ? "enabled" : "disabled", __func__); + if (compression_requested) { + // Any level would do, let's go for HIGH because we are overachievers. + context->set_compression_level(GRPC_COMPRESS_LEVEL_HIGH); + } else { + context->set_compression_level(GRPC_COMPRESS_LEVEL_NONE); + } + } + if (!CheckExpectedCompression(*context, + request->expect_compressed().value())) { + return Status(grpc::StatusCode::INVALID_ARGUMENT, + "Compressed request expectation not met."); + } if (request->response_size() > 0) { - if (!SetPayload(request->response_type(), request->response_size(), - response->mutable_payload())) { - return Status(grpc::StatusCode::INTERNAL, "Error creating payload."); + if (!SetPayload(request->response_size(), response->mutable_payload())) { + return Status(grpc::StatusCode::INVALID_ARGUMENT, + "Error creating payload."); } } @@ -187,17 +193,36 @@ class TestServiceImpl : public TestService::Service { Status StreamingOutputCall( ServerContext* context, const StreamingOutputCallRequest* request, ServerWriter<StreamingOutputCallResponse>* writer) { - SetResponseCompression(context, *request); StreamingOutputCallResponse response; bool write_success = true; for (int i = 0; write_success && i < request->response_parameters_size(); i++) { - if (!SetPayload(request->response_type(), - request->response_parameters(i).size(), + if (!SetPayload(request->response_parameters(i).size(), response.mutable_payload())) { - return Status(grpc::StatusCode::INTERNAL, "Error creating payload."); + return Status(grpc::StatusCode::INVALID_ARGUMENT, + "Error creating payload."); } - write_success = writer->Write(response); + WriteOptions wopts; + if (request->response_parameters(i).has_compressed()) { + // Compress by default. Disabled on a per-message basis. + context->set_compression_level(GRPC_COMPRESS_LEVEL_HIGH); + const bool compression_requested = + request->response_parameters(i).compressed().value(); + gpr_log(GPR_DEBUG, "Request for compression (%s) present for %s", + compression_requested ? "enabled" : "disabled", __func__); + if (!compression_requested) { + wopts.set_no_compression(); + } // else, compression is already enabled via the context. + } + int time_us; + if ((time_us = request->response_parameters(i).interval_us()) > 0) { + // Sleep before response if needed + gpr_timespec sleep_time = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(time_us, GPR_TIMESPAN)); + gpr_sleep_until(sleep_time); + } + write_success = writer->Write(response, wopts); } if (write_success) { return Status::OK; @@ -212,6 +237,11 @@ class TestServiceImpl : public TestService::Service { StreamingInputCallRequest request; int aggregated_payload_size = 0; while (reader->Read(&request)) { + if (!CheckExpectedCompression(*context, + request.expect_compressed().value())) { + return Status(grpc::StatusCode::INVALID_ARGUMENT, + "Compressed request expectation not met."); + } if (request.has_payload()) { aggregated_payload_size += request.payload().body().size(); } @@ -229,11 +259,18 @@ class TestServiceImpl : public TestService::Service { StreamingOutputCallResponse response; bool write_success = true; while (write_success && stream->Read(&request)) { - SetResponseCompression(context, request); if (request.response_parameters_size() != 0) { response.mutable_payload()->set_type(request.payload().type()); response.mutable_payload()->set_body( grpc::string(request.response_parameters(0).size(), '\0')); + int time_us; + if ((time_us = request.response_parameters(0).interval_us()) > 0) { + // Sleep before response if needed + gpr_timespec sleep_time = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(time_us, GPR_TIMESPAN)); + gpr_sleep_until(sleep_time); + } write_success = stream->Write(response); } } diff --git a/test/cpp/interop/metrics_client.cc b/test/cpp/interop/metrics_client.cc index c8c2215fab..7a0cb994df 100644 --- a/test/cpp/interop/metrics_client.cc +++ b/test/cpp/interop/metrics_client.cc @@ -76,7 +76,7 @@ bool PrintMetrics(std::unique_ptr<MetricsService::Stub> stub, bool total_only, while (reader->Read(&gauge_response)) { if (gauge_response.value_case() == GaugeResponse::kLongValue) { if (!total_only) { - gpr_log(GPR_INFO, "%s: %ld", gauge_response.name().c_str(), + gpr_log(GPR_INFO, "%s: %lld", gauge_response.name().c_str(), gauge_response.long_value()); } overall_qps += gauge_response.long_value(); diff --git a/test/cpp/interop/rnd.dat b/test/cpp/interop/rnd.dat Binary files differdeleted file mode 100644 index 8c7f38f9e0..0000000000 --- a/test/cpp/interop/rnd.dat +++ /dev/null diff --git a/test/cpp/interop/server_helper.cc b/test/cpp/interop/server_helper.cc index c6d891ad71..8b0b511bcb 100644 --- a/test/cpp/interop/server_helper.cc +++ b/test/cpp/interop/server_helper.cc @@ -72,6 +72,10 @@ uint32_t InteropServerContextInspector::GetEncodingsAcceptedByClient() const { return grpc_call_test_only_get_encodings_accepted_by_peer(context_.call_); } +uint32_t InteropServerContextInspector::GetMessageFlags() const { + return grpc_call_test_only_get_message_flags(context_.call_); +} + std::shared_ptr<const AuthContext> InteropServerContextInspector::GetAuthContext() const { return context_.auth_context(); diff --git a/test/cpp/interop/server_helper.h b/test/cpp/interop/server_helper.h index 12865e4032..a1da14a4c8 100644 --- a/test/cpp/interop/server_helper.h +++ b/test/cpp/interop/server_helper.h @@ -54,6 +54,7 @@ class InteropServerContextInspector { bool IsCancelled() const; grpc_compression_algorithm GetCallCompressionAlgorithm() const; uint32_t GetEncodingsAcceptedByClient() const; + uint32_t GetMessageFlags() const; private: const ::grpc::ServerContext& context_; diff --git a/test/cpp/interop/stress_interop_client.cc b/test/cpp/interop/stress_interop_client.cc index aa95682e74..1d5fc80cf2 100644 --- a/test/cpp/interop/stress_interop_client.cc +++ b/test/cpp/interop/stress_interop_client.cc @@ -138,8 +138,12 @@ bool StressTestInteropClient::RunTest(TestCaseType test_case) { is_success = interop_client_->DoLargeUnary(); break; } - case LARGE_COMPRESSED_UNARY: { - is_success = interop_client_->DoLargeCompressedUnary(); + case CLIENT_COMPRESSED_UNARY: { + is_success = interop_client_->DoClientCompressedUnary(); + break; + } + case CLIENT_COMPRESSED_STREAMING: { + is_success = interop_client_->DoClientCompressedStreaming(); break; } case CLIENT_STREAMING: { @@ -150,8 +154,12 @@ bool StressTestInteropClient::RunTest(TestCaseType test_case) { is_success = interop_client_->DoResponseStreaming(); break; } + case SERVER_COMPRESSED_UNARY: { + is_success = interop_client_->DoServerCompressedUnary(); + break; + } case SERVER_COMPRESSED_STREAMING: { - is_success = interop_client_->DoResponseCompressedStreaming(); + is_success = interop_client_->DoServerCompressedStreaming(); break; } case SLOW_CONSUMER: { diff --git a/test/cpp/interop/stress_interop_client.h b/test/cpp/interop/stress_interop_client.h index aa93b58b4a..cf6a713473 100644 --- a/test/cpp/interop/stress_interop_client.h +++ b/test/cpp/interop/stress_interop_client.h @@ -51,29 +51,33 @@ using std::vector; enum TestCaseType { UNKNOWN_TEST = -1, - EMPTY_UNARY = 0, - LARGE_UNARY = 1, - LARGE_COMPRESSED_UNARY = 2, - CLIENT_STREAMING = 3, - SERVER_STREAMING = 4, - SERVER_COMPRESSED_STREAMING = 5, - SLOW_CONSUMER = 6, - HALF_DUPLEX = 7, - PING_PONG = 8, - CANCEL_AFTER_BEGIN = 9, - CANCEL_AFTER_FIRST_RESPONSE = 10, - TIMEOUT_ON_SLEEPING_SERVER = 11, - EMPTY_STREAM = 12, - STATUS_CODE_AND_MESSAGE = 13, - CUSTOM_METADATA = 14 + EMPTY_UNARY, + LARGE_UNARY, + CLIENT_COMPRESSED_UNARY, + CLIENT_COMPRESSED_STREAMING, + CLIENT_STREAMING, + SERVER_STREAMING, + SERVER_COMPRESSED_UNARY, + SERVER_COMPRESSED_STREAMING, + SLOW_CONSUMER, + HALF_DUPLEX, + PING_PONG, + CANCEL_AFTER_BEGIN, + CANCEL_AFTER_FIRST_RESPONSE, + TIMEOUT_ON_SLEEPING_SERVER, + EMPTY_STREAM, + STATUS_CODE_AND_MESSAGE, + CUSTOM_METADATA }; const vector<pair<TestCaseType, grpc::string>> kTestCaseList = { {EMPTY_UNARY, "empty_unary"}, {LARGE_UNARY, "large_unary"}, - {LARGE_COMPRESSED_UNARY, "large_compressed_unary"}, + {CLIENT_COMPRESSED_UNARY, "client_compressed_unary"}, + {CLIENT_COMPRESSED_STREAMING, "client_compressed_streaming"}, {CLIENT_STREAMING, "client_streaming"}, {SERVER_STREAMING, "server_streaming"}, + {SERVER_COMPRESSED_UNARY, "server_compressed_unary"}, {SERVER_COMPRESSED_STREAMING, "server_compressed_streaming"}, {SLOW_CONSUMER, "slow_consumer"}, {HALF_DUPLEX, "half_duplex"}, diff --git a/test/cpp/qps/client.h b/test/cpp/qps/client.h index 2a89eb8018..047bd16408 100644 --- a/test/cpp/qps/client.h +++ b/test/cpp/qps/client.h @@ -125,13 +125,15 @@ class Client { if (reset) { Histogram* to_merge = new Histogram[threads_.size()]; for (size_t i = 0; i < threads_.size(); i++) { - threads_[i]->Swap(&to_merge[i]); - latencies.Merge(to_merge[i]); + threads_[i]->BeginSwap(&to_merge[i]); } - delete[] to_merge; - 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]); + } + delete[] to_merge; timer_result = timer->Mark(); } else { // merge snapshots of each thread histogram @@ -213,6 +215,7 @@ class Client { public: Thread(Client* client, size_t idx) : done_(false), + new_stats_(nullptr), client_(client), idx_(idx), impl_(&Thread::ThreadFunc, this) {} @@ -225,9 +228,16 @@ class Client { impl_.join(); } - void Swap(Histogram* n) { + void BeginSwap(Histogram* n) { std::lock_guard<std::mutex> g(mu_); - n->Swap(&histogram_); + new_stats_ = n; + } + + void EndSwap() { + std::unique_lock<std::mutex> g(mu_); + while (new_stats_ != nullptr) { + cv_.wait(g); + }; } void MergeStatsInto(Histogram* hist) { @@ -241,11 +251,10 @@ class Client { void ThreadFunc() { for (;;) { - // lock since the thread should only be doing one thing at a time - std::lock_guard<std::mutex> g(mu_); // run the loop body const bool thread_still_ok = client_->ThreadFunc(&histogram_, idx_); - // see if we're done + // lock, see if we're done + std::lock_guard<std::mutex> g(mu_); if (!thread_still_ok) { gpr_log(GPR_ERROR, "Finishing client thread due to RPC error"); done_ = true; @@ -253,11 +262,19 @@ class Client { if (done_) { return; } + // check if we're resetting stats, swap out the histogram if so + if (new_stats_) { + new_stats_->Swap(&histogram_); + new_stats_ = nullptr; + cv_.notify_one(); + } } } std::mutex mu_; + std::condition_variable cv_; bool done_; + Histogram* new_stats_; Histogram histogram_; Client* client_; const size_t idx_; diff --git a/test/cpp/qps/client_async.cc b/test/cpp/qps/client_async.cc index c32160a7d4..1507d1e3d6 100644 --- a/test/cpp/qps/client_async.cc +++ b/test/cpp/qps/client_async.cc @@ -42,7 +42,6 @@ #include <thread> #include <vector> -#include <gflags/gflags.h> #include <grpc++/alarm.h> #include <grpc++/channel.h> #include <grpc++/client_context.h> @@ -181,14 +180,14 @@ class AsyncClient : public ClientImpl<StubType, RequestType> { using namespace std::placeholders; int t = 0; - for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) { - for (int ch = 0; ch < config.client_channels(); ch++) { + for (int ch = 0; ch < config.client_channels(); ch++) { + for (int i = 0; i < config.outstanding_rpcs_per_channel(); i++) { auto* cq = cli_cqs_[t].get(); auto ctx = setup_ctx(channels_[ch].get_stub(), next_issuers_[t], request_); ctx->Start(cq); - t = (t + 1) % cli_cqs_.size(); } + t = (t + 1) % cli_cqs_.size(); } } virtual ~AsyncClient() { @@ -250,7 +249,8 @@ class AsyncUnaryClient GRPC_FINAL : public AsyncClient<BenchmarkService::Stub, SimpleRequest> { public: explicit AsyncUnaryClient(const ClientConfig& config) - : AsyncClient(config, SetupCtx, BenchmarkStubCreator) { + : AsyncClient<BenchmarkService::Stub, SimpleRequest>( + config, SetupCtx, BenchmarkStubCreator) { StartThreads(num_async_threads_); } ~AsyncUnaryClient() GRPC_OVERRIDE { EndThreads(); } @@ -377,7 +377,8 @@ class AsyncStreamingClient GRPC_FINAL : public AsyncClient<BenchmarkService::Stub, SimpleRequest> { public: explicit AsyncStreamingClient(const ClientConfig& config) - : AsyncClient(config, SetupCtx, BenchmarkStubCreator) { + : AsyncClient<BenchmarkService::Stub, SimpleRequest>( + config, SetupCtx, BenchmarkStubCreator) { StartThreads(num_async_threads_); } @@ -512,7 +513,8 @@ class GenericAsyncStreamingClient GRPC_FINAL : public AsyncClient<grpc::GenericStub, ByteBuffer> { public: explicit GenericAsyncStreamingClient(const ClientConfig& config) - : AsyncClient(config, SetupCtx, GenericStubCreator) { + : AsyncClient<grpc::GenericStub, ByteBuffer>(config, SetupCtx, + GenericStubCreator) { StartThreads(num_async_threads_); } diff --git a/test/cpp/qps/client_sync.cc b/test/cpp/qps/client_sync.cc index fb161f70ee..c88e95b80e 100644 --- a/test/cpp/qps/client_sync.cc +++ b/test/cpp/qps/client_sync.cc @@ -40,7 +40,6 @@ #include <thread> #include <vector> -#include <gflags/gflags.h> #include <grpc++/channel.h> #include <grpc++/client_context.h> #include <grpc++/server.h> diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc index 04b2b453f9..08bf045883 100644 --- a/test/cpp/qps/driver.cc +++ b/test/cpp/qps/driver.cc @@ -31,6 +31,7 @@ * */ +#include <cinttypes> #include <deque> #include <list> #include <thread> @@ -43,7 +44,6 @@ #include <grpc/support/alloc.h> #include <grpc/support/host_port.h> #include <grpc/support/log.h> -#include <gtest/gtest.h> #include "src/core/lib/support/env.h" #include "src/proto/grpc/testing/services.grpc.pb.h" @@ -245,8 +245,8 @@ std::unique_ptr<ScenarioResult> RunScenario( // where class contained in std::vector must have a copy constructor auto* servers = new ServerData[num_servers]; for (size_t i = 0; i < num_servers; i++) { - gpr_log(GPR_INFO, "Starting server on %s (worker #%d)", workers[i].c_str(), - i); + gpr_log(GPR_INFO, "Starting server on %s (worker #%" PRIuPTR ")", + workers[i].c_str(), i); servers[i].stub = WorkerService::NewStub( CreateChannel(workers[i], InsecureChannelCredentials())); @@ -308,8 +308,8 @@ std::unique_ptr<ScenarioResult> RunScenario( auto* clients = new ClientData[num_clients]; for (size_t i = 0; i < num_clients; i++) { const auto& worker = workers[i + num_servers]; - gpr_log(GPR_INFO, "Starting client on %s (worker #%d)", worker.c_str(), - i + num_servers); + gpr_log(GPR_INFO, "Starting client on %s (worker #%" PRIuPTR ")", + worker.c_str(), i + num_servers); clients[i].stub = WorkerService::NewStub( CreateChannel(worker, InsecureChannelCredentials())); ClientConfig per_client_config = client_config; diff --git a/test/cpp/qps/parse_json.cc b/test/cpp/qps/parse_json.cc index df7a62f0a0..e4fc35fa41 100644 --- a/test/cpp/qps/parse_json.cc +++ b/test/cpp/qps/parse_json.cc @@ -31,8 +31,6 @@ * */ -#include <grpc++/support/config_protobuf.h> - #include "test/cpp/qps/parse_json.h" #include <string> @@ -57,11 +55,26 @@ void ParseJson(const grpc::string& json, const grpc::string& type, grpc::string errmsg(status.error_message()); gpr_log(GPR_ERROR, "Failed to convert json to binary: errcode=%d msg=%s", status.error_code(), errmsg.c_str()); - gpr_log(GPR_ERROR, "JSON: ", json.c_str()); + gpr_log(GPR_ERROR, "JSON: %s", json.c_str()); abort(); } GPR_ASSERT(msg->ParseFromString(binary)); } +grpc::string SerializeJson(const GRPC_CUSTOM_MESSAGE& msg, + const grpc::string& type) { + std::unique_ptr<google::protobuf::util::TypeResolver> type_resolver( + google::protobuf::util::NewTypeResolverForDescriptorPool( + "type.googleapis.com", + google::protobuf::DescriptorPool::generated_pool())); + grpc::string binary; + grpc::string json_string; + msg.SerializeToString(&binary); + auto status = + BinaryToJsonString(type_resolver.get(), type, binary, &json_string); + GPR_ASSERT(status.ok()); + return json_string; +} + } // testing } // grpc diff --git a/test/cpp/qps/parse_json.h b/test/cpp/qps/parse_json.h index 4b8ca79f21..ce1821f961 100644 --- a/test/cpp/qps/parse_json.h +++ b/test/cpp/qps/parse_json.h @@ -34,8 +34,8 @@ #ifndef TEST_QPS_PARSE_JSON_H #define TEST_QPS_PARSE_JSON_H +#include <grpc++/impl/codegen/config_protobuf.h> #include <grpc++/support/config.h> -#include <grpc++/support/config_protobuf.h> namespace grpc { namespace testing { @@ -43,6 +43,9 @@ namespace testing { void ParseJson(const grpc::string& json, const grpc::string& type, GRPC_CUSTOM_MESSAGE* msg); +grpc::string SerializeJson(const GRPC_CUSTOM_MESSAGE& msg, + const grpc::string& type); + } // testing } // grpc diff --git a/test/cpp/qps/perf_db_client.cc b/test/cpp/qps/perf_db_client.cc deleted file mode 100644 index 98efd8c3e3..0000000000 --- a/test/cpp/qps/perf_db_client.cc +++ /dev/null @@ -1,140 +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 "test/cpp/qps/perf_db_client.h" - -namespace grpc { -namespace testing { - -// sets the client and server config information -void PerfDbClient::setConfigs(const ClientConfig& client_config, - const ServerConfig& server_config) { - client_config_ = client_config; - server_config_ = server_config; -} - -// sets the QPS -void PerfDbClient::setQps(double qps) { qps_ = qps; } - -// sets the QPS per core -void PerfDbClient::setQpsPerCore(double qps_per_core) { - qps_per_core_ = qps_per_core; -} - -// sets the 50th, 90th, 95th, 99th and 99.9th percentile latency -void PerfDbClient::setLatencies(double perc_lat_50, double perc_lat_90, - double perc_lat_95, double perc_lat_99, - double perc_lat_99_point_9) { - perc_lat_50_ = perc_lat_50; - perc_lat_90_ = perc_lat_90; - perc_lat_95_ = perc_lat_95; - perc_lat_99_ = perc_lat_99; - perc_lat_99_point_9_ = perc_lat_99_point_9; -} - -// sets the server and client, user and system times -void PerfDbClient::setTimes(double server_system_time, double server_user_time, - double client_system_time, - double client_user_time) { - server_system_time_ = server_system_time; - server_user_time_ = server_user_time; - client_system_time_ = client_system_time; - client_user_time_ = client_user_time; -} - -// sends the data to the performance database server -bool PerfDbClient::sendData(std::string hashed_id, std::string test_name, - std::string sys_info, std::string tag) { - // Data record request object - SingleUserRecordRequest single_user_record_request; - - // setting access token, name of the test and the system information - single_user_record_request.set_hashed_id(hashed_id); - single_user_record_request.set_test_name(test_name); - single_user_record_request.set_sys_info(sys_info); - single_user_record_request.set_tag(tag); - - // setting configs - *(single_user_record_request.mutable_client_config()) = client_config_; - *(single_user_record_request.mutable_server_config()) = server_config_; - - Metrics* metrics = single_user_record_request.mutable_metrics(); - - // setting metrcs in data record request - if (qps_ != DBL_MIN) { - metrics->set_qps(qps_); - } - if (qps_per_core_ != DBL_MIN) { - metrics->set_qps_per_core(qps_per_core_); - } - if (perc_lat_50_ != DBL_MIN) { - metrics->set_perc_lat_50(perc_lat_50_); - } - if (perc_lat_90_ != DBL_MIN) { - metrics->set_perc_lat_90(perc_lat_90_); - } - if (perc_lat_95_ != DBL_MIN) { - metrics->set_perc_lat_95(perc_lat_95_); - } - if (perc_lat_99_ != DBL_MIN) { - metrics->set_perc_lat_99(perc_lat_99_); - } - if (perc_lat_99_point_9_ != DBL_MIN) { - metrics->set_perc_lat_99_point_9(perc_lat_99_point_9_); - } - if (server_system_time_ != DBL_MIN) { - metrics->set_server_system_time(server_system_time_); - } - if (server_user_time_ != DBL_MIN) { - metrics->set_server_user_time(server_user_time_); - } - if (client_system_time_ != DBL_MIN) { - metrics->set_client_system_time(client_system_time_); - } - if (client_user_time_ != DBL_MIN) { - metrics->set_client_user_time(client_user_time_); - } - - SingleUserRecordReply single_user_record_reply; - ClientContext context; - - Status status = stub_->RecordSingleClientData( - &context, single_user_record_request, &single_user_record_reply); - if (status.ok()) { - return true; // data sent to database successfully - } else { - return false; // error in data sending - } -} -} // testing -} // grpc diff --git a/test/cpp/qps/perf_db_client.h b/test/cpp/qps/perf_db_client.h deleted file mode 100644 index b74c70d86b..0000000000 --- a/test/cpp/qps/perf_db_client.h +++ /dev/null @@ -1,113 +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 <cfloat> -#include <iostream> -#include <memory> -#include <string> - -#include <grpc++/channel.h> -#include <grpc++/client_context.h> -#include <grpc++/create_channel.h> -#include <grpc++/security/credentials.h> -#include <grpc++/support/channel_arguments.h> -#include <grpc/grpc.h> -#include "src/proto/grpc/testing/perf_db.grpc.pb.h" - -namespace grpc { -namespace testing { - -// Manages data sending to performance database server -class PerfDbClient { - public: - PerfDbClient() { - qps_ = DBL_MIN; - qps_per_core_ = DBL_MIN; - perc_lat_50_ = DBL_MIN; - perc_lat_90_ = DBL_MIN; - perc_lat_95_ = DBL_MIN; - perc_lat_99_ = DBL_MIN; - perc_lat_99_point_9_ = DBL_MIN; - server_system_time_ = DBL_MIN; - server_user_time_ = DBL_MIN; - client_system_time_ = DBL_MIN; - client_user_time_ = DBL_MIN; - } - - void init(std::shared_ptr<Channel> channel) { - stub_ = PerfDbTransfer::NewStub(channel); - } - - ~PerfDbClient() {} - - // sets the client and server config information - void setConfigs(const ClientConfig& client_config, - const ServerConfig& server_config); - - // sets the qps - void setQps(double qps); - - // sets the qps per core - void setQpsPerCore(double qps_per_core); - - // sets the 50th, 90th, 95th, 99th and 99.9th percentile latency - void setLatencies(double perc_lat_50, double perc_lat_90, double perc_lat_95, - double perc_lat_99, double perc_lat_99_point_9); - - // sets the server and client, user and system times - void setTimes(double server_system_time, double server_user_time, - double client_system_time, double client_user_time); - - // sends the data to the performance database server - bool sendData(std::string hashed_id, std::string test_name, - std::string sys_info, std::string tag); - - private: - std::unique_ptr<PerfDbTransfer::Stub> stub_; - ClientConfig client_config_; - ServerConfig server_config_; - double qps_; - double qps_per_core_; - double perc_lat_50_; - double perc_lat_90_; - double perc_lat_95_; - double perc_lat_99_; - double perc_lat_99_point_9_; - double server_system_time_; - double server_user_time_; - double client_system_time_; - double client_user_time_; -}; - -} // namespace testing -} // namespace grpc diff --git a/test/cpp/qps/qps_json_driver.cc b/test/cpp/qps/qps_json_driver.cc index d7642f0e1e..f5d739f893 100644 --- a/test/cpp/qps/qps_json_driver.cc +++ b/test/cpp/qps/qps_json_driver.cc @@ -34,7 +34,7 @@ #include <memory> #include <set> -#include <grpc++/support/config_protobuf.h> +#include <grpc++/impl/codegen/config_protobuf.h> #include <gflags/gflags.h> #include <grpc/support/log.h> diff --git a/test/cpp/qps/report.cc b/test/cpp/qps/report.cc index 3ae41399cf..2ec7d8676c 100644 --- a/test/cpp/qps/report.cc +++ b/test/cpp/qps/report.cc @@ -35,11 +35,9 @@ #include <fstream> -#include <google/protobuf/util/json_util.h> -#include <google/protobuf/util/type_resolver_util.h> - #include <grpc/support/log.h> #include "test/cpp/qps/driver.h" +#include "test/cpp/qps/parse_json.h" #include "test/cpp/qps/stats.h" namespace grpc { @@ -104,18 +102,8 @@ void GprLogReporter::ReportTimes(const ScenarioResult& result) { } void JsonReporter::ReportQPS(const ScenarioResult& result) { - std::unique_ptr<google::protobuf::util::TypeResolver> type_resolver( - google::protobuf::util::NewTypeResolverForDescriptorPool( - "type.googleapis.com", - google::protobuf::DescriptorPool::generated_pool())); - grpc::string binary; - grpc::string json_string; - result.SerializeToString(&binary); - auto status = BinaryToJsonString( - type_resolver.get(), "type.googleapis.com/grpc.testing.ScenarioResult", - binary, &json_string); - GPR_ASSERT(status.ok()); - + grpc::string json_string = + SerializeJson(result, "type.googleapis.com/grpc.testing.ScenarioResult"); std::ofstream output_file(report_file_); output_file << json_string; output_file.close(); diff --git a/test/cpp/qps/report.h b/test/cpp/qps/report.h index 8f04d84124..39cf498e7b 100644 --- a/test/cpp/qps/report.h +++ b/test/cpp/qps/report.h @@ -41,7 +41,6 @@ #include <grpc++/support/config.h> #include "test/cpp/qps/driver.h" -#include "test/cpp/qps/perf_db_client.h" namespace grpc { namespace testing { diff --git a/test/cpp/qps/server_async.cc b/test/cpp/qps/server_async.cc index 1eddb1dc1d..c9954d0d02 100644 --- a/test/cpp/qps/server_async.cc +++ b/test/cpp/qps/server_async.cc @@ -37,7 +37,6 @@ #include <mutex> #include <thread> -#include <gflags/gflags.h> #include <grpc++/generic/async_generic_service.h> #include <grpc++/security/server_credentials.h> #include <grpc++/server.h> @@ -48,7 +47,6 @@ #include <grpc/support/alloc.h> #include <grpc/support/host_port.h> #include <grpc/support/log.h> -#include <gtest/gtest.h> #include "src/proto/grpc/testing/services.grpc.pb.h" #include "test/core/util/test_config.h" diff --git a/test/cpp/qps/server_sync.cc b/test/cpp/qps/server_sync.cc index 9e64f470bf..c774985bfa 100644 --- a/test/cpp/qps/server_sync.cc +++ b/test/cpp/qps/server_sync.cc @@ -33,7 +33,6 @@ #include <thread> -#include <gflags/gflags.h> #include <grpc++/security/server_credentials.h> #include <grpc++/server.h> #include <grpc++/server_builder.h> diff --git a/test/cpp/util/byte_buffer_proto_helper.h b/test/cpp/util/byte_buffer_proto_helper.h index 42cea59e33..007723c691 100644 --- a/test/cpp/util/byte_buffer_proto_helper.h +++ b/test/cpp/util/byte_buffer_proto_helper.h @@ -36,8 +36,8 @@ #include <memory> +#include <grpc++/impl/codegen/config_protobuf.h> #include <grpc++/support/byte_buffer.h> -#include <grpc++/support/config_protobuf.h> namespace grpc { namespace testing { diff --git a/test/cpp/util/cli_call.cc b/test/cpp/util/cli_call.cc index 99fad7f2fe..98b9d930d6 100644 --- a/test/cpp/util/cli_call.cc +++ b/test/cpp/util/cli_call.cc @@ -86,7 +86,6 @@ Status CliCall::Call(std::shared_ptr<grpc::Channel> channel, cq.Next(&got_tag, &ok); if (!ok) { std::cout << "Failed to read response." << std::endl; - return Status(StatusCode::INTERNAL, "Failed to read response"); } grpc::Status status; call->Finish(&status, tag(5)); @@ -103,6 +102,7 @@ Status CliCall::Call(std::shared_ptr<grpc::Channel> channel, slices[i].size()); } } + *server_initial_metadata = ctx.GetServerInitialMetadata(); *server_trailing_metadata = ctx.GetServerTrailingMetadata(); return status; diff --git a/test/cpp/util/grpc_cli.cc b/test/cpp/util/grpc_cli.cc index 68cf4114a8..c52e48bae6 100644 --- a/test/cpp/util/grpc_cli.cc +++ b/test/cpp/util/grpc_cli.cc @@ -32,32 +32,33 @@ */ /* - A command line tool to talk to any grpc server. + A command line tool to talk to a grpc server. Example of talking to grpc interop server: - 1. Prepare request binary file: - a. create a text file input.txt, containing the following: - response_size: 10 - payload: { - body: "hello world" - } - b. under grpc/ run - protoc --proto_path=src/proto/grpc/testing/ \ - --encode=grpc.testing.SimpleRequest - src/proto/grpc/testing/messages.proto \ - < input.txt > input.bin - 2. Start a server - make interop_server && bins/opt/interop_server --port=50051 - 3. Run the tool - make grpc_cli && bins/opt/grpc_cli call localhost:50051 \ - /grpc.testing.TestService/UnaryCall --enable_ssl=false \ - --input_binary_file=input.bin --output_binary_file=output.bin - 4. Decode response - protoc --proto_path=src/proto/grpc/testing/ \ - --decode=grpc.testing.SimpleResponse src/proto/grpc/testing/messages.proto \ - < output.bin > output.txt - 5. Now the text form of response should be in output.txt - Optionally, metadata can be passed to server via flag --metadata, e.g. - --metadata="MyHeaderKey1:Value1:MyHeaderKey2:Value2" + grpc_cli call localhost:50051 UnaryCall src/proto/grpc/testing/test.proto \ + "response_size:10" --enable_ssl=false + + Options: + 1. --proto_path, if your proto file is not under current working directory, + use this flag to provide a search root. It should work similar to the + counterpart in protoc. + 2. --metadata specifies metadata to be sent to the server, such as: + --metadata="MyHeaderKey1:Value1:MyHeaderKey2:Value2" + 3. --enable_ssl, whether to use tls. + 4. --use_auth, if set to true, attach a GoogleDefaultCredentials to the call + 3. --input_binary_file, a file containing the serialized request. The file + can be generated by calling something like: + protoc --proto_path=src/proto/grpc/testing/ \ + --encode=grpc.testing.SimpleRequest \ + src/proto/grpc/testing/messages.proto \ + < input.txt > input.bin + If this is used and no proto file is provided in the argument list, the + method string has to be exact in the form of /package.service/method. + 4. --output_binary_file, a file to write binary format response into, it can + be later decoded using protoc: + protoc --proto_path=src/proto/grpc/testing/ \ + --decode=grpc.testing.SimpleResponse \ + src/proto/grpc/testing/messages.proto \ + < output.bin > output.txt */ #include <fstream> @@ -72,6 +73,7 @@ #include <grpc/grpc.h> #include "test/cpp/util/cli_call.h" +#include "test/cpp/util/proto_file_parser.h" #include "test/cpp/util/string_ref_helper.h" #include "test/cpp/util/test_config.h" @@ -79,10 +81,11 @@ DEFINE_bool(enable_ssl, true, "Whether to use ssl/tls."); DEFINE_bool(use_auth, false, "Whether to create default google credentials."); DEFINE_string(input_binary_file, "", "Path to input file containing serialized request."); -DEFINE_string(output_binary_file, "output.bin", +DEFINE_string(output_binary_file, "", "Path to output file to write serialized response."); DEFINE_string(metadata, "", "Metadata to send to server, in the form of key1:val1:key2:val2"); +DEFINE_string(proto_path, ".", "Path to look for the proto file."); void ParseMetadataFlag( std::multimap<grpc::string, grpc::string>* client_metadata) { @@ -126,28 +129,51 @@ void PrintMetadata(const T& m, const grpc::string& message) { int main(int argc, char** argv) { grpc::testing::InitTest(&argc, &argv, true); - if (argc < 4 || grpc::string(argv[1]) != "call") { - std::cout << "Usage: grpc_cli call server_host:port full_method_string\n" - << "Example: grpc_cli call service.googleapis.com " - << "/grpc.testing.TestService/UnaryCall " - << "--input_binary_file=input.bin --output_binary_file=output.bin" - << std::endl; + if (argc < 4 || argc == 5 || grpc::string(argv[1]) != "call") { + std::cout << "Usage: grpc_cli call server_host:port method_name " + << "[proto file] [text format request] [<options>]" << std::endl; } + + grpc::string file_name; + grpc::string request_text; grpc::string server_address(argv[2]); - // TODO(yangg) basic check of method string - grpc::string method(argv[3]); + grpc::string method_name(argv[3]); + std::unique_ptr<grpc::testing::ProtoFileParser> parser; + grpc::string serialized_request_proto; - if (FLAGS_input_binary_file.empty()) { - std::cout << "Missing --input_binary_file for serialized request." - << std::endl; + if (argc == 6) { + file_name = argv[4]; + // TODO(yangg) read from stdin as well? + request_text = argv[5]; + } + + if (request_text.empty() && FLAGS_input_binary_file.empty()) { + std::cout << "Missing input. Use text format input or " + << "--input_binary_file for serialized request" << std::endl; return 1; + } else if (!request_text.empty()) { + parser.reset(new grpc::testing::ProtoFileParser(FLAGS_proto_path, file_name, + method_name)); + method_name = parser->GetFullMethodName(); + if (parser->HasError()) { + return 1; + } } - std::cout << "connecting to " << server_address << std::endl; - std::ifstream input_file(FLAGS_input_binary_file, - std::ios::in | std::ios::binary); - std::stringstream input_stream; - input_stream << input_file.rdbuf(); + if (parser) { + serialized_request_proto = + parser->GetSerializedProto(request_text, true /* is_request */); + if (parser->HasError()) { + return 1; + } + } else if (!FLAGS_input_binary_file.empty()) { + std::ifstream input_file(FLAGS_input_binary_file, + std::ios::in | std::ios::binary); + std::stringstream input_stream; + input_stream << input_file.rdbuf(); + serialized_request_proto = input_stream.str(); + } + std::cout << "connecting to " << server_address << std::endl; std::shared_ptr<grpc::ChannelCredentials> creds; if (!FLAGS_enable_ssl) { @@ -162,25 +188,34 @@ int main(int argc, char** argv) { std::shared_ptr<grpc::Channel> channel = grpc::CreateChannel(server_address, creds); - grpc::string response; + grpc::string serialized_response_proto; std::multimap<grpc::string, grpc::string> client_metadata; std::multimap<grpc::string_ref, grpc::string_ref> server_initial_metadata, server_trailing_metadata; ParseMetadataFlag(&client_metadata); PrintMetadata(client_metadata, "Sending client initial metadata:"); grpc::Status s = grpc::testing::CliCall::Call( - channel, method, input_stream.str(), &response, client_metadata, - &server_initial_metadata, &server_trailing_metadata); + channel, method_name, serialized_request_proto, + &serialized_response_proto, client_metadata, &server_initial_metadata, + &server_trailing_metadata); PrintMetadata(server_initial_metadata, "Received initial metadata from server:"); PrintMetadata(server_trailing_metadata, "Received trailing metadata from server:"); if (s.ok()) { std::cout << "Rpc succeeded with OK status" << std::endl; - if (!response.empty()) { + if (parser) { + grpc::string response_text = parser->GetTextFormat( + serialized_response_proto, false /* is_request */); + if (parser->HasError()) { + return 1; + } + std::cout << "Response: \n " << response_text << std::endl; + } + if (!FLAGS_output_binary_file.empty()) { std::ofstream output_file(FLAGS_output_binary_file, std::ios::trunc | std::ios::binary); - output_file << response; + output_file << serialized_response_proto; } } else { std::cout << "Rpc failed with status code " << s.error_code() diff --git a/test/cpp/util/metrics_server.cc b/test/cpp/util/metrics_server.cc index cc6b39b753..1c7cd6382a 100644 --- a/test/cpp/util/metrics_server.cc +++ b/test/cpp/util/metrics_server.cc @@ -99,7 +99,7 @@ std::shared_ptr<QpsGauge> MetricsServiceImpl::CreateQpsGauge( std::lock_guard<std::mutex> lock(mu_); std::shared_ptr<QpsGauge> qps_gauge(new QpsGauge()); - const auto p = qps_gauges_.emplace(name, qps_gauge); + const auto p = qps_gauges_.insert(std::make_pair(name, qps_gauge)); // p.first is an iterator pointing to <name, shared_ptr<QpsGauge>> pair. // p.second is a boolean which is set to 'true' if the QpsGauge is @@ -114,7 +114,7 @@ std::shared_ptr<QpsGauge> MetricsServiceImpl::CreateQpsGauge( std::unique_ptr<grpc::Server> MetricsServiceImpl::StartServer(int port) { gpr_log(GPR_INFO, "Building metrics server.."); - const grpc::string address = "0.0.0.0:" + std::to_string(port); + const grpc::string address = "0.0.0.0:" + grpc::to_string(port); ServerBuilder builder; builder.AddListeningPort(address, grpc::InsecureServerCredentials()); diff --git a/test/cpp/util/proto_file_parser.cc b/test/cpp/util/proto_file_parser.cc new file mode 100644 index 0000000000..25aec329eb --- /dev/null +++ b/test/cpp/util/proto_file_parser.cc @@ -0,0 +1,178 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test/cpp/util/proto_file_parser.h" + +#include <algorithm> +#include <iostream> +#include <sstream> + +#include <google/protobuf/text_format.h> +#include <grpc++/support/config.h> + +namespace grpc { +namespace testing { +namespace { + +// Match the user input method string to the full_name from method descriptor. +bool MethodNameMatch(const grpc::string& full_name, const grpc::string& input) { + grpc::string clean_input = input; + std::replace(clean_input.begin(), clean_input.end(), '/', '.'); + if (clean_input.size() > full_name.size()) { + return false; + } + return full_name.compare(full_name.size() - clean_input.size(), + clean_input.size(), clean_input) == 0; +} +} // namespace + +class ErrorPrinter + : public google::protobuf::compiler::MultiFileErrorCollector { + public: + explicit ErrorPrinter(ProtoFileParser* parser) : parser_(parser) {} + + void AddError(const grpc::string& filename, int line, int column, + const grpc::string& message) GRPC_OVERRIDE { + std::ostringstream oss; + oss << "error " << filename << " " << line << " " << column << " " + << message << "\n"; + parser_->LogError(oss.str()); + } + + void AddWarning(const grpc::string& filename, int line, int column, + const grpc::string& message) GRPC_OVERRIDE { + std::cout << "warning " << filename << " " << line << " " << column << " " + << message << std::endl; + } + + private: + ProtoFileParser* parser_; // not owned +}; + +ProtoFileParser::ProtoFileParser(const grpc::string& proto_path, + const grpc::string& file_name, + const grpc::string& method) + : has_error_(false) { + source_tree_.MapPath("", proto_path); + error_printer_.reset(new ErrorPrinter(this)); + importer_.reset(new google::protobuf::compiler::Importer( + &source_tree_, error_printer_.get())); + const auto* file_desc = importer_->Import(file_name); + if (!file_desc) { + LogError(""); + return; + } + dynamic_factory_.reset( + new google::protobuf::DynamicMessageFactory(importer_->pool())); + + const google::protobuf::MethodDescriptor* method_descriptor = nullptr; + for (int i = 0; !method_descriptor && i < file_desc->service_count(); i++) { + const auto* service_desc = file_desc->service(i); + for (int j = 0; j < service_desc->method_count(); j++) { + const auto* method_desc = service_desc->method(j); + if (MethodNameMatch(method_desc->full_name(), method)) { + if (method_descriptor) { + std::ostringstream error_stream("Ambiguous method names: "); + error_stream << method_descriptor->full_name() << " "; + error_stream << method_desc->full_name(); + LogError(error_stream.str()); + } + method_descriptor = method_desc; + } + } + } + if (!method_descriptor) { + LogError("Method name not found"); + } + if (has_error_) { + return; + } + full_method_name_ = method_descriptor->full_name(); + size_t last_dot = full_method_name_.find_last_of('.'); + if (last_dot != grpc::string::npos) { + full_method_name_[last_dot] = '/'; + } + full_method_name_.insert(full_method_name_.begin(), '/'); + + request_prototype_.reset( + dynamic_factory_->GetPrototype(method_descriptor->input_type())->New()); + response_prototype_.reset( + dynamic_factory_->GetPrototype(method_descriptor->output_type())->New()); +} + +ProtoFileParser::~ProtoFileParser() {} + +grpc::string ProtoFileParser::GetSerializedProto( + const grpc::string& text_format_proto, bool is_request) { + grpc::string serialized; + grpc::protobuf::Message* msg = + is_request ? request_prototype_.get() : response_prototype_.get(); + bool ok = + google::protobuf::TextFormat::ParseFromString(text_format_proto, msg); + if (!ok) { + LogError("Failed to parse text format to proto."); + return ""; + } + ok = request_prototype_->SerializeToString(&serialized); + if (!ok) { + LogError("Failed to serialize proto."); + return ""; + } + return serialized; +} + +grpc::string ProtoFileParser::GetTextFormat( + const grpc::string& serialized_proto, bool is_request) { + grpc::protobuf::Message* msg = + is_request ? request_prototype_.get() : response_prototype_.get(); + if (!msg->ParseFromString(serialized_proto)) { + LogError("Failed to deserialize proto."); + return ""; + } + grpc::string text_format; + if (!google::protobuf::TextFormat::PrintToString(*msg, &text_format)) { + LogError("Failed to print proto message to text format"); + return ""; + } + return text_format; +} + +void ProtoFileParser::LogError(const grpc::string& error_msg) { + if (!error_msg.empty()) { + std::cout << error_msg << std::endl; + } + has_error_ = true; +} + +} // namespace testing +} // namespace grpc diff --git a/test/cpp/util/proto_file_parser.h b/test/cpp/util/proto_file_parser.h new file mode 100644 index 0000000000..46cdd66503 --- /dev/null +++ b/test/cpp/util/proto_file_parser.h @@ -0,0 +1,85 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_TEST_CPP_UTIL_PROTO_FILE_PARSER_H +#define GRPC_TEST_CPP_UTIL_PROTO_FILE_PARSER_H + +#include <memory> + +#include <google/protobuf/compiler/importer.h> +#include <google/protobuf/dynamic_message.h> + +#include "src/compiler/config.h" + +namespace grpc { +namespace testing { +class ErrorPrinter; + +// Find method and associated request/response types. +class ProtoFileParser { + public: + // The given proto file_name will be searched in a source tree rooted from + // proto_path. The method could be a partial string such as Service.Method or + // even just Method. It will log an error if there is ambiguity. + ProtoFileParser(const grpc::string& proto_path, const grpc::string& file_name, + const grpc::string& method); + ~ProtoFileParser(); + + grpc::string GetFullMethodName() const { return full_method_name_; } + + grpc::string GetSerializedProto(const grpc::string& text_format_proto, + bool is_request); + + grpc::string GetTextFormat(const grpc::string& serialized_proto, + bool is_request); + + bool HasError() const { return has_error_; } + + void LogError(const grpc::string& error_msg); + + private: + bool has_error_; + grpc::string request_text_; + grpc::string full_method_name_; + google::protobuf::compiler::DiskSourceTree source_tree_; + std::unique_ptr<ErrorPrinter> error_printer_; + std::unique_ptr<google::protobuf::compiler::Importer> importer_; + std::unique_ptr<google::protobuf::DynamicMessageFactory> dynamic_factory_; + std::unique_ptr<grpc::protobuf::Message> request_prototype_; + std::unique_ptr<grpc::protobuf::Message> response_prototype_; +}; + +} // namespace testing +} // namespace grpc + +#endif // GRPC_TEST_CPP_UTIL_PROTO_FILE_PARSER_H diff --git a/test/cpp/util/proto_reflection_descriptor_database.cc b/test/cpp/util/proto_reflection_descriptor_database.cc new file mode 100644 index 0000000000..25b720aee0 --- /dev/null +++ b/test/cpp/util/proto_reflection_descriptor_database.cc @@ -0,0 +1,316 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test/cpp/util/proto_reflection_descriptor_database.h" + +#include <vector> + +#include <grpc/support/log.h> + +using grpc::reflection::v1alpha::ServerReflection; +using grpc::reflection::v1alpha::ServerReflectionRequest; +using grpc::reflection::v1alpha::ServerReflectionResponse; +using grpc::reflection::v1alpha::ListServiceResponse; +using grpc::reflection::v1alpha::ErrorResponse; + +namespace grpc { + +ProtoReflectionDescriptorDatabase::ProtoReflectionDescriptorDatabase( + std::unique_ptr<ServerReflection::Stub> stub) + : stub_(std::move(stub)) {} + +ProtoReflectionDescriptorDatabase::ProtoReflectionDescriptorDatabase( + std::shared_ptr<grpc::Channel> channel) + : stub_(ServerReflection::NewStub(channel)) {} + +ProtoReflectionDescriptorDatabase::~ProtoReflectionDescriptorDatabase() {} + +bool ProtoReflectionDescriptorDatabase::FindFileByName( + const string& filename, google::protobuf::FileDescriptorProto* output) { + if (cached_db_.FindFileByName(filename, output)) { + return true; + } + + if (known_files_.find(filename) != known_files_.end()) { + return false; + } + + ServerReflectionRequest request; + request.set_file_by_filename(filename); + ServerReflectionResponse response; + + DoOneRequest(request, response); + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse) { + AddFileFromResponse(response.file_descriptor_response()); + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse error = response.error_response(); + if (error.error_code() == StatusCode::NOT_FOUND) { + gpr_log(GPR_INFO, "NOT_FOUND from server for FindFileByName(%s)", + filename.c_str()); + } else { + gpr_log(GPR_INFO, + "Error on FindFileByName(%s)\n\tError code: %d\n" + "\tError Message: %s", + filename.c_str(), error.error_code(), + error.error_message().c_str()); + } + } else { + gpr_log( + GPR_INFO, + "Error on FindFileByName(%s) response type\n" + "\tExpecting: %d\n\tReceived: %d", + filename.c_str(), + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse, + response.message_response_case()); + } + + return cached_db_.FindFileByName(filename, output); +} + +bool ProtoReflectionDescriptorDatabase::FindFileContainingSymbol( + const string& symbol_name, google::protobuf::FileDescriptorProto* output) { + if (cached_db_.FindFileContainingSymbol(symbol_name, output)) { + return true; + } + + if (missing_symbols_.find(symbol_name) != missing_symbols_.end()) { + return false; + } + + ServerReflectionRequest request; + request.set_file_containing_symbol(symbol_name); + ServerReflectionResponse response; + + DoOneRequest(request, response); + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse) { + AddFileFromResponse(response.file_descriptor_response()); + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse error = response.error_response(); + if (error.error_code() == StatusCode::NOT_FOUND) { + missing_symbols_.insert(symbol_name); + gpr_log(GPR_INFO, + "NOT_FOUND from server for FindFileContainingSymbol(%s)", + symbol_name.c_str()); + } else { + gpr_log(GPR_INFO, + "Error on FindFileContainingSymbol(%s)\n" + "\tError code: %d\n\tError Message: %s", + symbol_name.c_str(), error.error_code(), + error.error_message().c_str()); + } + } else { + gpr_log( + GPR_INFO, + "Error on FindFileContainingSymbol(%s) response type\n" + "\tExpecting: %d\n\tReceived: %d", + symbol_name.c_str(), + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse, + response.message_response_case()); + } + return cached_db_.FindFileContainingSymbol(symbol_name, output); +} + +bool ProtoReflectionDescriptorDatabase::FindFileContainingExtension( + const string& containing_type, int field_number, + google::protobuf::FileDescriptorProto* output) { + if (cached_db_.FindFileContainingExtension(containing_type, field_number, + output)) { + return true; + } + + if (missing_extensions_.find(containing_type) != missing_extensions_.end() && + missing_extensions_[containing_type].find(field_number) != + missing_extensions_[containing_type].end()) { + gpr_log(GPR_INFO, "nested map."); + return false; + } + + ServerReflectionRequest request; + request.mutable_file_containing_extension()->set_containing_type( + containing_type); + request.mutable_file_containing_extension()->set_extension_number( + field_number); + ServerReflectionResponse response; + + DoOneRequest(request, response); + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse) { + AddFileFromResponse(response.file_descriptor_response()); + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse error = response.error_response(); + if (error.error_code() == StatusCode::NOT_FOUND) { + if (missing_extensions_.find(containing_type) == + missing_extensions_.end()) { + missing_extensions_[containing_type] = {}; + } + missing_extensions_[containing_type].insert(field_number); + gpr_log(GPR_INFO, + "NOT_FOUND from server for FindFileContainingExtension(%s, %d)", + containing_type.c_str(), field_number); + } else { + gpr_log(GPR_INFO, + "Error on FindFileContainingExtension(%s, %d)\n" + "\tError code: %d\n\tError Message: %s", + containing_type.c_str(), field_number, error.error_code(), + error.error_message().c_str()); + } + } else { + gpr_log( + GPR_INFO, + "Error on FindFileContainingExtension(%s, %d) response type\n" + "\tExpecting: %d\n\tReceived: %d", + containing_type.c_str(), field_number, + ServerReflectionResponse::MessageResponseCase::kFileDescriptorResponse, + response.message_response_case()); + } + + return cached_db_.FindFileContainingExtension(containing_type, field_number, + output); +} + +bool ProtoReflectionDescriptorDatabase::FindAllExtensionNumbers( + const string& extendee_type, std::vector<int>* output) { + if (cached_extension_numbers_.find(extendee_type) != + cached_extension_numbers_.end()) { + *output = cached_extension_numbers_[extendee_type]; + return true; + } + + ServerReflectionRequest request; + request.set_all_extension_numbers_of_type(extendee_type); + ServerReflectionResponse response; + + DoOneRequest(request, response); + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase:: + kAllExtensionNumbersResponse) { + auto number = response.all_extension_numbers_response().extension_number(); + *output = std::vector<int>(number.begin(), number.end()); + cached_extension_numbers_[extendee_type] = *output; + return true; + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse error = response.error_response(); + if (error.error_code() == StatusCode::NOT_FOUND) { + gpr_log(GPR_INFO, "NOT_FOUND from server for FindAllExtensionNumbers(%s)", + extendee_type.c_str()); + } else { + gpr_log(GPR_INFO, + "Error on FindAllExtensionNumbersExtension(%s)\n" + "\tError code: %d\n\tError Message: %s", + extendee_type.c_str(), error.error_code(), + error.error_message().c_str()); + } + } + return false; +} + +bool ProtoReflectionDescriptorDatabase::GetServices( + std::vector<std::string>* output) { + ServerReflectionRequest request; + request.set_list_services(""); + ServerReflectionResponse response; + + DoOneRequest(request, response); + + if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kListServicesResponse) { + const ListServiceResponse ls_response = response.list_services_response(); + for (int i = 0; i < ls_response.service_size(); ++i) { + (*output).push_back(ls_response.service(i).name()); + } + return true; + } else if (response.message_response_case() == + ServerReflectionResponse::MessageResponseCase::kErrorResponse) { + const ErrorResponse error = response.error_response(); + gpr_log(GPR_INFO, + "Error on GetServices()\n\tError code: %d\n" + "\tError Message: %s", + error.error_code(), error.error_message().c_str()); + } else { + gpr_log( + GPR_INFO, + "Error on GetServices() response type\n\tExpecting: %d\n\tReceived: %d", + ServerReflectionResponse::MessageResponseCase::kListServicesResponse, + response.message_response_case()); + } + return false; +} + +const google::protobuf::FileDescriptorProto +ProtoReflectionDescriptorDatabase::ParseFileDescriptorProtoResponse( + const std::string& byte_fd_proto) { + google::protobuf::FileDescriptorProto file_desc_proto; + file_desc_proto.ParseFromString(byte_fd_proto); + return file_desc_proto; +} + +void ProtoReflectionDescriptorDatabase::AddFileFromResponse( + const grpc::reflection::v1alpha::FileDescriptorResponse& response) { + for (int i = 0; i < response.file_descriptor_proto_size(); ++i) { + const google::protobuf::FileDescriptorProto file_proto = + ParseFileDescriptorProtoResponse(response.file_descriptor_proto(i)); + if (known_files_.find(file_proto.name()) == known_files_.end()) { + known_files_.insert(file_proto.name()); + cached_db_.Add(file_proto); + } + } +} + +const std::shared_ptr<ProtoReflectionDescriptorDatabase::ClientStream> +ProtoReflectionDescriptorDatabase::GetStream() { + if (!stream_) { + stream_ = stub_->ServerReflectionInfo(&ctx_); + } + return stream_; +} + +void ProtoReflectionDescriptorDatabase::DoOneRequest( + const ServerReflectionRequest& request, + ServerReflectionResponse& response) { + stream_mutex_.lock(); + GetStream()->Write(request); + GetStream()->Read(&response); + stream_mutex_.unlock(); +} + +} // namespace grpc diff --git a/test/cpp/util/proto_reflection_descriptor_database.h b/test/cpp/util/proto_reflection_descriptor_database.h new file mode 100644 index 0000000000..99c00675bb --- /dev/null +++ b/test/cpp/util/proto_reflection_descriptor_database.h @@ -0,0 +1,131 @@ +/* + * + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ +#ifndef GRPC_TEST_CPP_PROTO_SERVER_REFLECTION_DATABSE_H +#define GRPC_TEST_CPP_PROTO_SERVER_REFLECTION_DATABSE_H + +#include <mutex> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include <google/protobuf/descriptor.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/descriptor_database.h> +#include <grpc++/ext/reflection.grpc.pb.h> +#include <grpc++/grpc++.h> + +namespace grpc { + +// ProtoReflectionDescriptorDatabase takes a stub of ServerReflection and +// provides the methods defined by DescriptorDatabase interfaces. It can be used +// to feed a DescriptorPool instance. +class ProtoReflectionDescriptorDatabase + : public google::protobuf::DescriptorDatabase { + public: + explicit ProtoReflectionDescriptorDatabase( + std::unique_ptr<reflection::v1alpha::ServerReflection::Stub> stub); + + explicit ProtoReflectionDescriptorDatabase( + std::shared_ptr<grpc::Channel> channel); + + virtual ~ProtoReflectionDescriptorDatabase(); + + // The following four methods implement DescriptorDatabase interfaces. + // + // 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, + google::protobuf::FileDescriptorProto* output) + GRPC_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, + google::protobuf::FileDescriptorProto* output) + GRPC_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, + // otherwise returns false and leaves *output undefined. containing_type + // must be a fully-qualified type name. + bool FindFileContainingExtension( + const string& containing_type, int field_number, + google::protobuf::FileDescriptorProto* output) GRPC_OVERRIDE; + + // Finds the tag numbers used by all known extensions of + // extendee_type, and appends them to output in an undefined + // order. This method is best-effort: it's not guaranteed that the + // database will find all extensions, and it's not guaranteed that + // FindFileContainingExtension will return true on all of the found + // 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; + + // Provide a list of full names of registered services + bool GetServices(std::vector<std::string>* output); + + private: + typedef ClientReaderWriter< + grpc::reflection::v1alpha::ServerReflectionRequest, + grpc::reflection::v1alpha::ServerReflectionResponse> + ClientStream; + + const google::protobuf::FileDescriptorProto ParseFileDescriptorProtoResponse( + const std::string& byte_fd_proto); + + void AddFileFromResponse( + const grpc::reflection::v1alpha::FileDescriptorResponse& response); + + const std::shared_ptr<ClientStream> GetStream(); + + void DoOneRequest( + const grpc::reflection::v1alpha::ServerReflectionRequest& request, + grpc::reflection::v1alpha::ServerReflectionResponse& response); + + std::shared_ptr<ClientStream> stream_; + grpc::ClientContext ctx_; + std::unique_ptr<grpc::reflection::v1alpha::ServerReflection::Stub> stub_; + std::unordered_set<string> known_files_; + std::unordered_set<string> missing_symbols_; + std::unordered_map<string, std::unordered_set<int>> missing_extensions_; + std::unordered_map<string, std::vector<int>> cached_extension_numbers_; + std::mutex stream_mutex_; + + google::protobuf::SimpleDescriptorDatabase cached_db_; +}; + +} // namespace grpc + +#endif // GRPC_TEST_CPP_METRICS_SERVER_H diff --git a/test/cpp/util/test_credentials_provider.cc b/test/cpp/util/test_credentials_provider.cc index 9c09a73115..6e68f59e6a 100644 --- a/test/cpp/util/test_credentials_provider.cc +++ b/test/cpp/util/test_credentials_provider.cc @@ -41,15 +41,9 @@ #include "test/core/end2end/data/ssl_test_data.h" +namespace grpc { namespace { -using grpc::ChannelArguments; -using grpc::ChannelCredentials; -using grpc::InsecureChannelCredentials; -using grpc::InsecureServerCredentials; -using grpc::ServerCredentials; -using grpc::SslCredentialsOptions; -using grpc::SslServerCredentialsOptions; using grpc::testing::CredentialTypeProvider; // Provide test credentials. Thread-safe. @@ -69,19 +63,27 @@ class CredentialsProvider { class DefaultCredentialsProvider : public CredentialsProvider { public: - ~DefaultCredentialsProvider() override {} + ~DefaultCredentialsProvider() GRPC_OVERRIDE {} - void AddSecureType( - const grpc::string& type, - std::unique_ptr<CredentialTypeProvider> type_provider) override { + void AddSecureType(const grpc::string& type, + std::unique_ptr<CredentialTypeProvider> type_provider) + GRPC_OVERRIDE { // This clobbers any existing entry for type, except the defaults, which // can't be clobbered. grpc::unique_lock<grpc::mutex> lock(mu_); - added_secure_types_[type] = std::move(type_provider); + auto it = std::find(added_secure_type_names_.begin(), + added_secure_type_names_.end(), type); + if (it == added_secure_type_names_.end()) { + added_secure_type_names_.push_back(type); + added_secure_type_providers_.push_back(std::move(type_provider)); + } else { + added_secure_type_providers_[it - added_secure_type_names_.begin()] = + std::move(type_provider); + } } std::shared_ptr<ChannelCredentials> GetChannelCredentials( - const grpc::string& type, ChannelArguments* args) override { + const grpc::string& type, ChannelArguments* args) GRPC_OVERRIDE { if (type == grpc::testing::kInsecureCredentialsType) { return InsecureChannelCredentials(); } else if (type == grpc::testing::kTlsCredentialsType) { @@ -90,17 +92,19 @@ class DefaultCredentialsProvider : public CredentialsProvider { return SslCredentials(ssl_opts); } else { grpc::unique_lock<grpc::mutex> lock(mu_); - auto it(added_secure_types_.find(type)); - if (it == added_secure_types_.end()) { + auto it(std::find(added_secure_type_names_.begin(), + added_secure_type_names_.end(), type)); + if (it == added_secure_type_names_.end()) { gpr_log(GPR_ERROR, "Unsupported credentials type %s.", type.c_str()); return nullptr; } - return it->second->GetChannelCredentials(args); + return added_secure_type_providers_[it - added_secure_type_names_.begin()] + ->GetChannelCredentials(args); } } std::shared_ptr<ServerCredentials> GetServerCredentials( - const grpc::string& type) override { + const grpc::string& type) GRPC_OVERRIDE { if (type == grpc::testing::kInsecureCredentialsType) { return InsecureServerCredentials(); } else if (type == grpc::testing::kTlsCredentialsType) { @@ -112,28 +116,32 @@ class DefaultCredentialsProvider : public CredentialsProvider { return SslServerCredentials(ssl_opts); } else { grpc::unique_lock<grpc::mutex> lock(mu_); - auto it(added_secure_types_.find(type)); - if (it == added_secure_types_.end()) { + auto it(std::find(added_secure_type_names_.begin(), + added_secure_type_names_.end(), type)); + if (it == added_secure_type_names_.end()) { gpr_log(GPR_ERROR, "Unsupported credentials type %s.", type.c_str()); return nullptr; } - return it->second->GetServerCredentials(); + return added_secure_type_providers_[it - added_secure_type_names_.begin()] + ->GetServerCredentials(); } } - std::vector<grpc::string> GetSecureCredentialsTypeList() override { + std::vector<grpc::string> GetSecureCredentialsTypeList() GRPC_OVERRIDE { std::vector<grpc::string> types; types.push_back(grpc::testing::kTlsCredentialsType); grpc::unique_lock<grpc::mutex> lock(mu_); - for (const auto& type_pair : added_secure_types_) { - types.push_back(type_pair.first); + for (auto it = added_secure_type_names_.begin(); + it != added_secure_type_names_.end(); it++) { + types.push_back(*it); } return types; } private: grpc::mutex mu_; - std::unordered_map<grpc::string, std::unique_ptr<CredentialTypeProvider> > - added_secure_types_; + 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; @@ -148,7 +156,6 @@ CredentialsProvider* GetProvider() { } // namespace -namespace grpc { namespace testing { void AddSecureType(const grpc::string& type, |