| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 | /* * * Copyright 2019 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *     http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */#include <algorithm>#include <condition_variable>#include <functional>#include <memory>#include <mutex>#include <sstream>#include <thread>#include <google/protobuf/arena.h>#include <grpc/impl/codegen/log.h>#include <gtest/gtest.h>#include <grpcpp/channel.h>#include <grpcpp/client_context.h>#include <grpcpp/create_channel.h>#include <grpcpp/server.h>#include <grpcpp/server_builder.h>#include <grpcpp/server_context.h>#include <grpcpp/support/client_callback.h>#include <grpcpp/support/message_allocator.h>#include "src/core/lib/iomgr/iomgr.h"#include "src/proto/grpc/testing/echo.grpc.pb.h"#include "test/core/util/port.h"#include "test/core/util/test_config.h"#include "test/cpp/util/test_credentials_provider.h"// MAYBE_SKIP_TEST is a macro to determine if this particular test configuration// should be skipped based on a decision made at SetUp time. In particular, any// callback tests can only be run if the iomgr can run in the background or if// the transport is in-process.#define MAYBE_SKIP_TEST \  do {                  \    if (do_not_test_) { \      return;           \    }                   \  } while (0)namespace grpc {namespace testing {namespace {class CallbackTestServiceImpl    : public EchoTestService::ExperimentalCallbackService { public:  explicit CallbackTestServiceImpl() {}  void SetAllocatorMutator(      std::function<void(experimental::RpcAllocatorState* allocator_state,                         const EchoRequest* req, EchoResponse* resp)>          mutator) {    allocator_mutator_ = mutator;  }  experimental::ServerUnaryReactor* Echo(      experimental::CallbackServerContext* context, const EchoRequest* request,      EchoResponse* response) override {    response->set_message(request->message());    if (allocator_mutator_) {      allocator_mutator_(context->GetRpcAllocatorState(), request, response);    }    auto* reactor = context->DefaultReactor();    reactor->Finish(Status::OK);    return reactor;  } private:  std::function<void(experimental::RpcAllocatorState* allocator_state,                     const EchoRequest* req, EchoResponse* resp)>      allocator_mutator_;};enum class Protocol { INPROC, TCP };class TestScenario { public:  TestScenario(Protocol protocol, const grpc::string& creds_type)      : protocol(protocol), credentials_type(creds_type) {}  void Log() const;  Protocol protocol;  const grpc::string credentials_type;};static std::ostream& operator<<(std::ostream& out,                                const TestScenario& scenario) {  return out << "TestScenario{protocol="             << (scenario.protocol == Protocol::INPROC ? "INPROC" : "TCP")             << "," << scenario.credentials_type << "}";}void TestScenario::Log() const {  std::ostringstream out;  out << *this;  gpr_log(GPR_INFO, "%s", out.str().c_str());}class MessageAllocatorEnd2endTestBase    : public ::testing::TestWithParam<TestScenario> { protected:  MessageAllocatorEnd2endTestBase() {    GetParam().Log();    if (GetParam().protocol == Protocol::TCP) {      if (!grpc_iomgr_run_in_background()) {        do_not_test_ = true;        return;      }    }  }  ~MessageAllocatorEnd2endTestBase() = default;  void CreateServer(      experimental::MessageAllocator<EchoRequest, EchoResponse>* allocator) {    ServerBuilder builder;    auto server_creds = GetCredentialsProvider()->GetServerCredentials(        GetParam().credentials_type);    if (GetParam().protocol == Protocol::TCP) {      picked_port_ = grpc_pick_unused_port_or_die();      server_address_ << "localhost:" << picked_port_;      builder.AddListeningPort(server_address_.str(), server_creds);    }    callback_service_.SetMessageAllocatorFor_Echo(allocator);    builder.RegisterService(&callback_service_);    server_ = builder.BuildAndStart();    is_server_started_ = true;  }  void ResetStub() {    ChannelArguments args;    auto channel_creds = GetCredentialsProvider()->GetChannelCredentials(        GetParam().credentials_type, &args);    switch (GetParam().protocol) {      case Protocol::TCP:        channel_ = ::grpc::CreateCustomChannel(server_address_.str(),                                               channel_creds, args);        break;      case Protocol::INPROC:        channel_ = server_->InProcessChannel(args);        break;      default:        assert(false);    }    stub_ = EchoTestService::NewStub(channel_);  }  void TearDown() override {    if (is_server_started_) {      server_->Shutdown();    }    if (picked_port_ > 0) {      grpc_recycle_unused_port(picked_port_);    }  }  void SendRpcs(int num_rpcs) {    grpc::string test_string("");    for (int i = 0; i < num_rpcs; i++) {      EchoRequest request;      EchoResponse response;      ClientContext cli_ctx;      test_string += grpc::string(1024, 'x');      request.set_message(test_string);      grpc::string val;      cli_ctx.set_compression_algorithm(GRPC_COMPRESS_GZIP);      std::mutex mu;      std::condition_variable cv;      bool done = false;      stub_->experimental_async()->Echo(          &cli_ctx, &request, &response,          [&request, &response, &done, &mu, &cv, val](Status s) {            GPR_ASSERT(s.ok());            EXPECT_EQ(request.message(), response.message());            std::lock_guard<std::mutex> l(mu);            done = true;            cv.notify_one();          });      std::unique_lock<std::mutex> l(mu);      while (!done) {        cv.wait(l);      }    }  }  bool do_not_test_{false};  bool is_server_started_{false};  int picked_port_{0};  std::shared_ptr<Channel> channel_;  std::unique_ptr<EchoTestService::Stub> stub_;  CallbackTestServiceImpl callback_service_;  std::unique_ptr<Server> server_;  std::ostringstream server_address_;};class NullAllocatorTest : public MessageAllocatorEnd2endTestBase {};TEST_P(NullAllocatorTest, SimpleRpc) {  MAYBE_SKIP_TEST;  CreateServer(nullptr);  ResetStub();  SendRpcs(1);}class SimpleAllocatorTest : public MessageAllocatorEnd2endTestBase { public:  class SimpleAllocator      : public experimental::MessageAllocator<EchoRequest, EchoResponse> {   public:    class MessageHolderImpl        : public experimental::MessageHolder<EchoRequest, EchoResponse> {     public:      MessageHolderImpl(int* request_deallocation_count,                        int* messages_deallocation_count)          : request_deallocation_count_(request_deallocation_count),            messages_deallocation_count_(messages_deallocation_count) {        set_request(new EchoRequest);        set_response(new EchoResponse);      }      void Release() override {        (*messages_deallocation_count_)++;        delete request();        delete response();        delete this;      }      void FreeRequest() override {        (*request_deallocation_count_)++;        delete request();        set_request(nullptr);      }      EchoRequest* ReleaseRequest() {        auto* ret = request();        set_request(nullptr);        return ret;      }     private:      int* request_deallocation_count_;      int* messages_deallocation_count_;    };    experimental::MessageHolder<EchoRequest, EchoResponse>* AllocateMessages()        override {      allocation_count++;      return new MessageHolderImpl(&request_deallocation_count,                                   &messages_deallocation_count);    }    int allocation_count = 0;    int request_deallocation_count = 0;    int messages_deallocation_count = 0;  };};TEST_P(SimpleAllocatorTest, SimpleRpc) {  MAYBE_SKIP_TEST;  const int kRpcCount = 10;  std::unique_ptr<SimpleAllocator> allocator(new SimpleAllocator);  CreateServer(allocator.get());  ResetStub();  SendRpcs(kRpcCount);  EXPECT_EQ(kRpcCount, allocator->allocation_count);  EXPECT_EQ(kRpcCount, allocator->messages_deallocation_count);  EXPECT_EQ(0, allocator->request_deallocation_count);}TEST_P(SimpleAllocatorTest, RpcWithEarlyFreeRequest) {  MAYBE_SKIP_TEST;  const int kRpcCount = 10;  std::unique_ptr<SimpleAllocator> allocator(new SimpleAllocator);  auto mutator = [](experimental::RpcAllocatorState* allocator_state,                    const EchoRequest* req, EchoResponse* resp) {    auto* info =        static_cast<SimpleAllocator::MessageHolderImpl*>(allocator_state);    EXPECT_EQ(req, info->request());    EXPECT_EQ(resp, info->response());    allocator_state->FreeRequest();    EXPECT_EQ(nullptr, info->request());  };  callback_service_.SetAllocatorMutator(mutator);  CreateServer(allocator.get());  ResetStub();  SendRpcs(kRpcCount);  EXPECT_EQ(kRpcCount, allocator->allocation_count);  EXPECT_EQ(kRpcCount, allocator->messages_deallocation_count);  EXPECT_EQ(kRpcCount, allocator->request_deallocation_count);}TEST_P(SimpleAllocatorTest, RpcWithReleaseRequest) {  MAYBE_SKIP_TEST;  const int kRpcCount = 10;  std::unique_ptr<SimpleAllocator> allocator(new SimpleAllocator);  std::vector<EchoRequest*> released_requests;  auto mutator = [&released_requests](                     experimental::RpcAllocatorState* allocator_state,                     const EchoRequest* req, EchoResponse* resp) {    auto* info =        static_cast<SimpleAllocator::MessageHolderImpl*>(allocator_state);    EXPECT_EQ(req, info->request());    EXPECT_EQ(resp, info->response());    released_requests.push_back(info->ReleaseRequest());    EXPECT_EQ(nullptr, info->request());  };  callback_service_.SetAllocatorMutator(mutator);  CreateServer(allocator.get());  ResetStub();  SendRpcs(kRpcCount);  EXPECT_EQ(kRpcCount, allocator->allocation_count);  EXPECT_EQ(kRpcCount, allocator->messages_deallocation_count);  EXPECT_EQ(0, allocator->request_deallocation_count);  EXPECT_EQ(static_cast<unsigned>(kRpcCount), released_requests.size());  for (auto* req : released_requests) {    delete req;  }}class ArenaAllocatorTest : public MessageAllocatorEnd2endTestBase { public:  class ArenaAllocator      : public experimental::MessageAllocator<EchoRequest, EchoResponse> {   public:    class MessageHolderImpl        : public experimental::MessageHolder<EchoRequest, EchoResponse> {     public:      MessageHolderImpl() {        set_request(            google::protobuf::Arena::CreateMessage<EchoRequest>(&arena_));        set_response(            google::protobuf::Arena::CreateMessage<EchoResponse>(&arena_));      }      void Release() override { delete this; }      void FreeRequest() override { GPR_ASSERT(0); }     private:      google::protobuf::Arena arena_;    };    experimental::MessageHolder<EchoRequest, EchoResponse>* AllocateMessages()        override {      allocation_count++;      return new MessageHolderImpl;    }    int allocation_count = 0;  };};TEST_P(ArenaAllocatorTest, SimpleRpc) {  MAYBE_SKIP_TEST;  const int kRpcCount = 10;  std::unique_ptr<ArenaAllocator> allocator(new ArenaAllocator);  CreateServer(allocator.get());  ResetStub();  SendRpcs(kRpcCount);  EXPECT_EQ(kRpcCount, allocator->allocation_count);}std::vector<TestScenario> CreateTestScenarios(bool test_insecure) {  std::vector<TestScenario> scenarios;  std::vector<grpc::string> credentials_types{      GetCredentialsProvider()->GetSecureCredentialsTypeList()};  auto insec_ok = [] {    // Only allow insecure credentials type when it is registered with the    // provider. User may create providers that do not have insecure.    return GetCredentialsProvider()->GetChannelCredentials(               kInsecureCredentialsType, nullptr) != nullptr;  };  if (test_insecure && insec_ok()) {    credentials_types.push_back(kInsecureCredentialsType);  }  GPR_ASSERT(!credentials_types.empty());  Protocol parr[]{Protocol::INPROC, Protocol::TCP};  for (Protocol p : parr) {    for (const auto& cred : credentials_types) {      // TODO(vjpai): Test inproc with secure credentials when feasible      if (p == Protocol::INPROC &&          (cred != kInsecureCredentialsType || !insec_ok())) {        continue;      }      scenarios.emplace_back(p, cred);    }  }  return scenarios;}INSTANTIATE_TEST_SUITE_P(NullAllocatorTest, NullAllocatorTest,                         ::testing::ValuesIn(CreateTestScenarios(true)));INSTANTIATE_TEST_SUITE_P(SimpleAllocatorTest, SimpleAllocatorTest,                         ::testing::ValuesIn(CreateTestScenarios(true)));INSTANTIATE_TEST_SUITE_P(ArenaAllocatorTest, ArenaAllocatorTest,                         ::testing::ValuesIn(CreateTestScenarios(true)));}  // namespace}  // namespace testing}  // namespace grpcint main(int argc, char** argv) {  grpc::testing::TestEnvironment env(argc, argv);  // The grpc_init is to cover the MAYBE_SKIP_TEST.  grpc_init();  ::testing::InitGoogleTest(&argc, argv);  int ret = RUN_ALL_TESTS();  grpc_shutdown();  return ret;}
 |