ServerConfigSelector, provider and filter (#27717)

* ServerConfigSelector, provider and filter

* Reviewer comments

* Reviewer comments

* Add some tests

* Fix include guards

* Regenerate projects and fix sanity check

* Reviewer comments
pull/27746/head
Yash Tibrewal 3 years ago committed by GitHub
parent fbf9801b83
commit e2b044d6d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 33
      BUILD
  2. 37
      CMakeLists.txt
  3. 12
      build_autogenerated.yaml
  4. 67
      src/core/ext/filters/server_config_selector/server_config_selector.cc
  5. 70
      src/core/ext/filters/server_config_selector/server_config_selector.h
  6. 265
      src/core/ext/filters/server_config_selector/server_config_selector_filter.cc
  7. 32
      src/core/ext/filters/server_config_selector/server_config_selector_filter.h
  8. 2
      src/core/ext/service_config/service_config_call_data.h
  9. 35
      test/core/server_config_selector/BUILD
  10. 81
      test/core/server_config_selector/server_config_selector_test.cc
  11. 24
      tools/run_tests/generated/tests.json

33
BUILD

@ -1986,6 +1986,39 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "grpc_server_config_selector",
srcs = [
"src/core/ext/filters/server_config_selector/server_config_selector.cc",
],
hdrs = [
"src/core/ext/filters/server_config_selector/server_config_selector.h",
],
language = "c++",
deps = [
"gpr_base",
"grpc_base",
"grpc_service_config",
],
)
grpc_cc_library(
name = "grpc_server_config_selector_filter",
srcs = [
"src/core/ext/filters/server_config_selector/server_config_selector_filter.cc",
],
hdrs = [
"src/core/ext/filters/server_config_selector/server_config_selector_filter.h",
],
language = "c++",
deps = [
"gpr_base",
"grpc_base",
"grpc_server_config_selector",
"grpc_service_config",
],
)
grpc_cc_library(
name = "idle_filter_state",
srcs = [

37
CMakeLists.txt generated

@ -899,6 +899,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx server_builder_with_socket_mutator_test)
endif()
add_dependencies(buildtests_cxx server_chttp2_test)
add_dependencies(buildtests_cxx server_config_selector_test)
add_dependencies(buildtests_cxx server_context_test_spouse_test)
add_dependencies(buildtests_cxx server_early_return_test)
add_dependencies(buildtests_cxx server_interceptors_end2end_test)
@ -14357,6 +14358,42 @@ target_link_libraries(server_chttp2_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(server_config_selector_test
src/core/ext/filters/server_config_selector/server_config_selector.cc
test/core/server_config_selector/server_config_selector_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(server_config_selector_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(server_config_selector_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)

@ -7311,6 +7311,18 @@ targets:
- test/core/surface/server_chttp2_test.cc
deps:
- grpc_test_util
- name: server_config_selector_test
gtest: true
build: test
language: c++
headers:
- src/core/ext/filters/server_config_selector/server_config_selector.h
src:
- src/core/ext/filters/server_config_selector/server_config_selector.cc
- test/core/server_config_selector/server_config_selector_test.cc
deps:
- grpc_test_util
uses_polling: false
- name: server_context_test_spouse_test
gtest: true
build: test

@ -0,0 +1,67 @@
//
// Copyright 2021 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 <grpc/support/port_platform.h>
#include "src/core/ext/filters/server_config_selector/server_config_selector.h"
#include "src/core/lib/channel/channel_args.h"
namespace grpc_core {
namespace {
void* ServerConfigSelectorProviderArgCopy(void* p) {
ServerConfigSelectorProvider* arg =
static_cast<ServerConfigSelectorProvider*>(p);
return arg->Ref().release();
}
void ServerConfigSelectorProviderArgDestroy(void* p) {
ServerConfigSelectorProvider* arg =
static_cast<ServerConfigSelectorProvider*>(p);
arg->Unref();
}
int ServerConfigSelectorProviderArgCmp(void* p, void* q) {
return QsortCompare(p, q);
}
const grpc_arg_pointer_vtable kChannelArgVtable = {
ServerConfigSelectorProviderArgCopy, ServerConfigSelectorProviderArgDestroy,
ServerConfigSelectorProviderArgCmp};
const char* kServerConfigSelectorProviderChannelArgName =
"grpc.internal.server_config_selector_provider";
} // namespace
grpc_arg ServerConfigSelectorProvider::MakeChannelArg() const {
return grpc_channel_arg_pointer_create(
const_cast<char*>(kServerConfigSelectorProviderChannelArgName),
const_cast<ServerConfigSelectorProvider*>(this), &kChannelArgVtable);
}
RefCountedPtr<ServerConfigSelectorProvider>
ServerConfigSelectorProvider::GetFromChannelArgs(
const grpc_channel_args& args) {
ServerConfigSelectorProvider* config_selector_provider =
grpc_channel_args_find_pointer<ServerConfigSelectorProvider>(
&args, kServerConfigSelectorProviderChannelArgName);
return config_selector_provider != nullptr ? config_selector_provider->Ref()
: nullptr;
}
} // namespace grpc_core

@ -0,0 +1,70 @@
//
// Copyright 2021 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.
//
#ifndef GRPC_CORE_EXT_FILTERS_SERVER_CONFIG_SELECTOR_SERVER_CONFIG_SELECTOR_H
#define GRPC_CORE_EXT_FILTERS_SERVER_CONFIG_SELECTOR_SERVER_CONFIG_SELECTOR_H
#include <grpc/support/port_platform.h>
#include "absl/status/statusor.h"
#include "src/core/ext/service_config/service_config.h"
#include "src/core/lib/transport/metadata_batch.h"
namespace grpc_core {
// ServerConfigSelector allows for choosing the service config to apply to a
// server-side call based on the received initial metadata.
class ServerConfigSelector : public RefCounted<ServerConfigSelector> {
public:
// Configuration to apply to an incoming call
struct CallConfig {
grpc_error_handle error = GRPC_ERROR_NONE;
const ServiceConfigParser::ParsedConfigVector* method_configs = nullptr;
RefCountedPtr<ServiceConfig> service_config;
};
~ServerConfigSelector() override = default;
// Returns the CallConfig to apply to a call based on the incoming \a metadata
virtual CallConfig GetCallConfig(grpc_metadata_batch* metadata) = 0;
};
// ServerConfigSelectorProvider allows for subscribers to watch for updates on
// ServerConfigSelector. It is propagated via channel args.
class ServerConfigSelectorProvider
: public RefCounted<ServerConfigSelectorProvider> {
public:
class ServerConfigSelectorWatcher {
public:
virtual ~ServerConfigSelectorWatcher() = default;
virtual void OnServerConfigSelectorUpdate(
absl::StatusOr<RefCountedPtr<ServerConfigSelector>> update) = 0;
};
~ServerConfigSelectorProvider() override = default;
// Only a single watcher is allowed at present
virtual absl::StatusOr<RefCountedPtr<ServerConfigSelector>> Watch(
std::unique_ptr<ServerConfigSelectorWatcher> watcher) = 0;
virtual void CancelWatch() = 0;
grpc_arg MakeChannelArg() const;
static RefCountedPtr<ServerConfigSelectorProvider> GetFromChannelArgs(
const grpc_channel_args& args);
};
} // namespace grpc_core
#endif // GRPC_CORE_EXT_FILTERS_SERVER_CONFIG_SELECTOR_SERVER_CONFIG_SELECTOR_H

@ -0,0 +1,265 @@
//
//
// Copyright 2021 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 <grpc/support/port_platform.h>
#include "src/core/ext/filters/server_config_selector/server_config_selector_filter.h"
#include "src/core/ext/filters/server_config_selector/server_config_selector.h"
#include "src/core/ext/service_config/service_config_call_data.h"
#include "src/core/lib/transport/error_utils.h"
namespace grpc_core {
namespace {
class ChannelData {
public:
static grpc_error_handle Init(grpc_channel_element* elem,
grpc_channel_element_args* args);
static void Destroy(grpc_channel_element* elem);
absl::StatusOr<RefCountedPtr<ServerConfigSelector>> config_selector() {
MutexLock lock(&mu_);
return config_selector_;
}
private:
class ServerConfigSelectorWatcher
: public ServerConfigSelectorProvider::ServerConfigSelectorWatcher {
public:
explicit ServerConfigSelectorWatcher(ChannelData* chand) : chand_(chand) {}
void OnServerConfigSelectorUpdate(
absl::StatusOr<RefCountedPtr<ServerConfigSelector>> update) override {
MutexLock lock(&chand_->mu_);
chand_->config_selector_ = std::move(update);
}
private:
ChannelData* chand_;
};
explicit ChannelData(RefCountedPtr<ServerConfigSelectorProvider>
server_config_selector_provider);
~ChannelData();
RefCountedPtr<ServerConfigSelectorProvider> server_config_selector_provider_;
Mutex mu_;
absl::StatusOr<RefCountedPtr<ServerConfigSelector>> config_selector_
ABSL_GUARDED_BY(mu_);
};
class CallData {
public:
static grpc_error_handle Init(grpc_call_element* elem,
const grpc_call_element_args* args);
static void Destroy(grpc_call_element* elem,
const grpc_call_final_info* /* final_info */,
grpc_closure* /* then_schedule_closure */);
static void StartTransportStreamOpBatch(grpc_call_element* elem,
grpc_transport_stream_op_batch* op);
private:
CallData(grpc_call_element* elem, const grpc_call_element_args& args);
~CallData();
static void RecvInitialMetadataReady(void* user_data,
grpc_error_handle error);
static void RecvTrailingMetadataReady(void* user_data,
grpc_error_handle error);
void MaybeResumeRecvTrailingMetadataReady();
grpc_call_context_element* call_context_;
grpc_core::CallCombiner* call_combiner_;
ServiceConfigCallData service_config_call_data_;
// Overall error for the call
grpc_error_handle error_ = GRPC_ERROR_NONE;
// State for keeping track of recv_initial_metadata
grpc_metadata_batch* recv_initial_metadata_ = nullptr;
grpc_closure* original_recv_initial_metadata_ready_ = nullptr;
grpc_closure recv_initial_metadata_ready_;
// State for keeping of track of recv_trailing_metadata
grpc_closure* original_recv_trailing_metadata_ready_;
grpc_closure recv_trailing_metadata_ready_;
grpc_error_handle recv_trailing_metadata_ready_error_;
bool seen_recv_trailing_metadata_ready_ = false;
};
// ChannelData
grpc_error_handle ChannelData::Init(grpc_channel_element* elem,
grpc_channel_element_args* args) {
GPR_ASSERT(elem->filter = &kServerConfigSelectorFilter);
RefCountedPtr<ServerConfigSelectorProvider> server_config_selector_provider =
ServerConfigSelectorProvider::GetFromChannelArgs(*args->channel_args);
if (server_config_selector_provider == nullptr) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"No ServerConfigSelectorProvider object found");
}
new (elem->channel_data)
ChannelData(std::move(server_config_selector_provider));
return GRPC_ERROR_NONE;
}
void ChannelData::Destroy(grpc_channel_element* elem) {
auto* chand = static_cast<ChannelData*>(elem->channel_data);
chand->~ChannelData();
}
ChannelData::ChannelData(
RefCountedPtr<ServerConfigSelectorProvider> server_config_selector_provider)
: server_config_selector_provider_(
std::move(server_config_selector_provider)) {
GPR_ASSERT(server_config_selector_provider_ != nullptr);
auto server_config_selector_watcher =
absl::make_unique<ServerConfigSelectorWatcher>(this);
config_selector_ = server_config_selector_provider_->Watch(
std::move(server_config_selector_watcher));
}
ChannelData::~ChannelData() { server_config_selector_provider_->CancelWatch(); }
// CallData
grpc_error_handle CallData::Init(grpc_call_element* elem,
const grpc_call_element_args* args) {
new (elem->call_data) CallData(elem, *args);
return GRPC_ERROR_NONE;
}
void CallData::Destroy(grpc_call_element* elem,
const grpc_call_final_info* /*final_info*/,
grpc_closure* /*then_schedule_closure*/) {
auto* calld = static_cast<CallData*>(elem->call_data);
calld->~CallData();
}
void CallData::StartTransportStreamOpBatch(grpc_call_element* elem,
grpc_transport_stream_op_batch* op) {
CallData* calld = static_cast<CallData*>(elem->call_data);
if (op->recv_initial_metadata) {
calld->recv_initial_metadata_ =
op->payload->recv_initial_metadata.recv_initial_metadata;
calld->original_recv_initial_metadata_ready_ =
op->payload->recv_initial_metadata.recv_initial_metadata_ready;
op->payload->recv_initial_metadata.recv_initial_metadata_ready =
&calld->recv_initial_metadata_ready_;
}
if (op->recv_trailing_metadata) {
// We might generate errors on receiving initial metadata which we need to
// bubble up through recv_trailing_metadata_ready
calld->original_recv_trailing_metadata_ready_ =
op->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
op->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
&calld->recv_trailing_metadata_ready_;
}
// Chain to the next filter.
grpc_call_next_op(elem, op);
}
CallData::CallData(grpc_call_element* elem, const grpc_call_element_args& args)
: call_context_(args.context), call_combiner_(args.call_combiner) {
GRPC_CLOSURE_INIT(&recv_initial_metadata_ready_, RecvInitialMetadataReady,
elem, grpc_schedule_on_exec_ctx);
GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready_, RecvTrailingMetadataReady,
elem, grpc_schedule_on_exec_ctx);
}
CallData::~CallData() {
// Remove the entry from call context, just in case anyone above us
// tries to look at it during call stack destruction.
call_context_[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA].value = nullptr;
GRPC_ERROR_UNREF(error_);
}
void CallData::RecvInitialMetadataReady(void* user_data,
grpc_error_handle error) {
grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
CallData* calld = static_cast<CallData*>(elem->call_data);
ChannelData* chand = static_cast<ChannelData*>(elem->channel_data);
if (error == GRPC_ERROR_NONE) {
auto config_selector = chand->config_selector();
if (config_selector.ok()) {
auto call_config =
config_selector.value()->GetCallConfig(calld->recv_initial_metadata_);
if (call_config.error != GRPC_ERROR_NONE) {
calld->error_ = call_config.error;
error = call_config.error; // Does not take a ref
} else {
calld->service_config_call_data_ =
ServiceConfigCallData(std::move(call_config.service_config),
call_config.method_configs, {});
calld->call_context_[GRPC_CONTEXT_SERVICE_CONFIG_CALL_DATA].value =
&calld->service_config_call_data_;
}
} else {
calld->error_ = absl_status_to_grpc_error(config_selector.status());
error = calld->error_;
}
}
calld->MaybeResumeRecvTrailingMetadataReady();
grpc_closure* closure = calld->original_recv_initial_metadata_ready_;
calld->original_recv_initial_metadata_ready_ = nullptr;
Closure::Run(DEBUG_LOCATION, closure, GRPC_ERROR_REF(error));
}
void CallData::RecvTrailingMetadataReady(void* user_data,
grpc_error_handle error) {
grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
CallData* calld = static_cast<CallData*>(elem->call_data);
if (calld->original_recv_initial_metadata_ready_ != nullptr) {
calld->seen_recv_trailing_metadata_ready_ = true;
calld->recv_trailing_metadata_ready_error_ = GRPC_ERROR_REF(error);
GRPC_CALL_COMBINER_STOP(calld->call_combiner_,
"Deferring RecvTrailingMetadataReady until after "
"RecvInitialMetadataReady");
return;
}
error = grpc_error_add_child(GRPC_ERROR_REF(error), calld->error_);
calld->error_ = GRPC_ERROR_NONE;
grpc_closure* closure = calld->original_recv_trailing_metadata_ready_;
calld->original_recv_trailing_metadata_ready_ = nullptr;
Closure::Run(DEBUG_LOCATION, closure, error);
}
void CallData::MaybeResumeRecvTrailingMetadataReady() {
if (seen_recv_trailing_metadata_ready_) {
seen_recv_trailing_metadata_ready_ = false;
grpc_error_handle error = recv_trailing_metadata_ready_error_;
recv_trailing_metadata_ready_error_ = GRPC_ERROR_NONE;
GRPC_CALL_COMBINER_START(call_combiner_, &recv_trailing_metadata_ready_,
error, "Continuing RecvTrailingMetadataReady");
}
}
} // namespace
const grpc_channel_filter kServerConfigSelectorFilter = {
CallData::StartTransportStreamOpBatch,
grpc_channel_next_op,
sizeof(CallData),
CallData::Init,
grpc_call_stack_ignore_set_pollset_or_pollset_set,
CallData::Destroy,
sizeof(ChannelData),
ChannelData::Init,
ChannelData::Destroy,
grpc_channel_next_get_info,
"server_config_selector_filter",
};
} // namespace grpc_core

@ -0,0 +1,32 @@
//
//
// Copyright 2021 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.
//
//
#ifndef GRPC_CORE_EXT_FILTERS_SERVER_CONFIG_SELECTOR_SERVER_CONFIG_SELECTOR_FILTER_H
#define GRPC_CORE_EXT_FILTERS_SERVER_CONFIG_SELECTOR_SERVER_CONFIG_SELECTOR_FILTER_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/channel/channel_stack.h"
namespace grpc_core {
extern const grpc_channel_filter kServerConfigSelectorFilter;
} // namespace grpc_core
#endif // GRPC_CORE_EXT_FILTERS_SERVER_CONFIG_SELECTOR_SERVER_CONFIG_SELECTOR_FILTER_H

@ -38,6 +38,8 @@ class ServiceConfigCallData {
public:
using CallAttributes = std::map<const char*, absl::string_view>;
ServiceConfigCallData() : method_configs_(nullptr) {}
ServiceConfigCallData(
RefCountedPtr<ServiceConfig> service_config,
const ServiceConfigParser::ParsedConfigVector* method_configs,

@ -0,0 +1,35 @@
# Copyright 2021 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.
load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package")
licenses(["notice"])
grpc_package(name = "test/core/server_config_selector")
grpc_cc_test(
name = "server_config_selector_test",
srcs = ["server_config_selector_test.cc"],
external_deps = [
"gtest",
],
language = "C++",
uses_polling = False,
deps = [
"//:gpr",
"//:grpc",
"//:grpc_server_config_selector",
"//test/core/util:grpc_test_util",
],
)

@ -0,0 +1,81 @@
//
//
// Copyright 2021 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 "src/core/ext/filters/server_config_selector/server_config_selector.h"
#include <gtest/gtest.h>
#include <grpc/grpc.h>
#include <grpc/support/log.h>
#include "src/core/lib/channel/channel_args.h"
#include "test/core/util/test_config.h"
namespace grpc {
namespace testing {
namespace {
using grpc_core::ServerConfigSelector;
using grpc_core::ServerConfigSelectorProvider;
class TestServerConfigSelectorProvider : public ServerConfigSelectorProvider {
absl::StatusOr<grpc_core::RefCountedPtr<ServerConfigSelector>> Watch(
std::unique_ptr<ServerConfigSelectorWatcher> /* watcher */) override {
return absl::UnavailableError("Test ServerConfigSelector");
}
void CancelWatch() override {}
};
// Test that ServerConfigSelectorProvider can be safely copied to channel args
// and destroyed
TEST(ServerConfigSelectorProviderTest, CopyChannelArgs) {
auto server_config_selector_provider =
grpc_core::MakeRefCounted<TestServerConfigSelectorProvider>();
grpc_arg arg = server_config_selector_provider->MakeChannelArg();
grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1);
EXPECT_EQ(server_config_selector_provider,
ServerConfigSelectorProvider::GetFromChannelArgs(*args));
grpc_channel_args_destroy(args);
}
// Test compare on channel args with the same ServerConfigSelectorProvider
TEST(ServerConfigSelectorProviderTest, ChannelArgsCompare) {
auto server_config_selector_provider =
grpc_core::MakeRefCounted<TestServerConfigSelectorProvider>();
grpc_arg arg = server_config_selector_provider->MakeChannelArg();
grpc_channel_args* args = grpc_channel_args_copy_and_add(nullptr, &arg, 1);
grpc_channel_args* new_args = grpc_channel_args_copy(args);
EXPECT_EQ(ServerConfigSelectorProvider::GetFromChannelArgs(*new_args),
ServerConfigSelectorProvider::GetFromChannelArgs(*args));
grpc_channel_args_destroy(args);
grpc_channel_args_destroy(new_args);
}
} // namespace
} // namespace testing
} // namespace grpc
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(argc, argv);
grpc_init();
int ret = RUN_ALL_TESTS();
grpc_shutdown();
return ret;
}

@ -6221,6 +6221,30 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "server_config_selector_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save