blob: 402760bdf203b11db01676917088edc227ed8589 [file] [log] [blame]
//
// 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/strings/numbers.h"
#include "absl/strings/str_format.h"
#include "src/core/ext/xds/xds_bootstrap_grpc.h"
#include "src/core/ext/xds/xds_client_grpc.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gpr/env.h"
#include "src/core/lib/gpr/tmpfile.h"
#include "src/core/lib/security/certificate_provider/certificate_provider_registry.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
namespace testing {
namespace {
TEST(XdsBootstrapTest, Basic) {
gpr_setenv("GRPC_EXPERIMENTAL_XDS_FEDERATION", "true");
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\","
" \"ignore\": 0"
" }"
" ],"
" \"ignore\": 0"
" },"
" {"
" \"server_uri\": \"ignored\","
" \"channel_creds\": ["
" {"
" \"type\": \"ignored\","
" \"ignore\": 0"
" },"
" {"
" \"type\": \"fake\""
" }"
" ],"
" \"ignore\": 0"
" }"
" ],"
" \"authorities\": {"
" \"xds.example.com\": {"
" \"client_listener_resource_name_template\": "
"\"xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/server/"
"%s\","
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///xds_server\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\""
" }"
" ],"
" \"server_features\": [\"xds_v3\"]"
" }"
" ]"
" },"
" \"xds.example2.com\": {"
" \"client_listener_resource_name_template\": "
"\"xdstp://xds.example2.com/envoy.config.listener.v3.Listener/grpc/"
"server/%s\","
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///xds_server2\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\""
" }"
" ],"
" \"server_features\": [\"xds_v3\"]"
" }"
" ]"
" }"
" },"
" \"node\": {"
" \"id\": \"foo\","
" \"cluster\": \"bar\","
" \"locality\": {"
" \"region\": \"milky_way\","
" \"zone\": \"sol_system\","
" \"sub_zone\": \"earth\","
" \"ignore\": {}"
" },"
" \"metadata\": {"
" \"foo\": 1,"
" \"bar\": 2"
" },"
" \"ignore\": \"whee\""
" },"
" \"server_listener_resource_name_template\": \"example/resource\","
" \"ignore\": {}"
"}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
auto bootstrap = std::move(*bootstrap_or);
auto* server =
&static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(bootstrap->server());
EXPECT_EQ(server->server_uri(), "fake:///lb");
EXPECT_EQ(server->channel_creds_type(), "fake");
EXPECT_TRUE(server->channel_creds_config().empty())
<< Json{server->channel_creds_config()}.Dump();
EXPECT_EQ(bootstrap->authorities().size(), 2);
auto* authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
bootstrap->LookupAuthority("xds.example.com"));
ASSERT_NE(authority, nullptr);
EXPECT_EQ(authority->client_listener_resource_name_template(),
"xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/"
"server/%s");
server =
static_cast<const GrpcXdsBootstrap::GrpcXdsServer*>(authority->server());
ASSERT_NE(server, nullptr);
EXPECT_EQ(server->server_uri(), "fake:///xds_server");
EXPECT_EQ(server->channel_creds_type(), "fake");
EXPECT_TRUE(server->channel_creds_config().empty())
<< Json{server->channel_creds_config()}.Dump();
authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
bootstrap->LookupAuthority("xds.example2.com"));
ASSERT_NE(authority, nullptr);
EXPECT_EQ(authority->client_listener_resource_name_template(),
"xdstp://xds.example2.com/envoy.config.listener.v3.Listener/grpc/"
"server/%s");
server =
static_cast<const GrpcXdsBootstrap::GrpcXdsServer*>(authority->server());
ASSERT_NE(server, nullptr);
EXPECT_EQ(server->server_uri(), "fake:///xds_server2");
EXPECT_EQ(server->channel_creds_type(), "fake");
EXPECT_TRUE(server->channel_creds_config().empty())
<< Json{server->channel_creds_config()}.Dump();
ASSERT_NE(bootstrap->node(), nullptr);
EXPECT_EQ(bootstrap->node()->id(), "foo");
EXPECT_EQ(bootstrap->node()->cluster(), "bar");
EXPECT_EQ(bootstrap->node()->locality_region(), "milky_way");
EXPECT_EQ(bootstrap->node()->locality_zone(), "sol_system");
EXPECT_EQ(bootstrap->node()->locality_sub_zone(), "earth");
EXPECT_THAT(bootstrap->node()->metadata(),
::testing::ElementsAre(
::testing::Pair(
::testing::Eq("bar"),
::testing::AllOf(
::testing::Property(&Json::type, Json::Type::NUMBER),
::testing::Property(&Json::string_value, "2"))),
::testing::Pair(
::testing::Eq("foo"),
::testing::AllOf(
::testing::Property(&Json::type, Json::Type::NUMBER),
::testing::Property(&Json::string_value, "1")))));
EXPECT_EQ(bootstrap->server_listener_resource_name_template(),
"example/resource");
gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION");
}
TEST(XdsBootstrapTest, ValidWithoutNode) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"fake\"}]"
" }"
" ]"
"}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
auto bootstrap = std::move(*bootstrap_or);
auto* server =
&static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(bootstrap->server());
EXPECT_EQ(server->server_uri(), "fake:///lb");
EXPECT_EQ(server->channel_creds_type(), "fake");
EXPECT_EQ(bootstrap->node(), nullptr);
}
TEST(XdsBootstrapTest, InsecureCreds) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"insecure\"}]"
" }"
" ]"
"}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
auto bootstrap = std::move(*bootstrap_or);
auto* server =
&static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(bootstrap->server());
EXPECT_EQ(server->server_uri(), "fake:///lb");
EXPECT_EQ(server->channel_creds_type(), "insecure");
EXPECT_EQ(bootstrap->node(), nullptr);
}
TEST(XdsBootstrapTest, GoogleDefaultCreds) {
// Generate call creds file needed by GoogleDefaultCreds.
const char token_str[] =
"{ \"client_id\": \"32555999999.apps.googleusercontent.com\","
" \"client_secret\": \"EmssLNjJy1332hD4KFsecret\","
" \"refresh_token\": \"1/Blahblasj424jladJDSGNf-u4Sua3HDA2ngjd42\","
" \"type\": \"authorized_user\"}";
char* creds_file_name;
FILE* creds_file = gpr_tmpfile("xds_bootstrap_test", &creds_file_name);
ASSERT_NE(creds_file_name, nullptr);
ASSERT_NE(creds_file, nullptr);
ASSERT_EQ(fwrite(token_str, 1, sizeof(token_str), creds_file),
sizeof(token_str));
fclose(creds_file);
gpr_setenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR, creds_file_name);
gpr_free(creds_file_name);
// Now run test.
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"google_default\"}]"
" }"
" ]"
"}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
auto bootstrap = std::move(*bootstrap_or);
auto* server =
&static_cast<const GrpcXdsBootstrap::GrpcXdsServer&>(bootstrap->server());
EXPECT_EQ(server->server_uri(), "fake:///lb");
EXPECT_EQ(server->channel_creds_type(), "google_default");
EXPECT_EQ(bootstrap->node(), nullptr);
}
TEST(XdsBootstrapTest, MissingChannelCreds) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\""
" }"
" ]"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:xds_servers[0].channel_creds error:field not present]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, NoKnownChannelCreds) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"unknown\"}]"
" }"
" ]"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:xds_servers[0].channel_creds "
"error:no known creds type found]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, MissingXdsServers) {
auto bootstrap = GrpcXdsBootstrap::Create("{}");
EXPECT_EQ(
bootstrap.status().message(),
"errors validating JSON: [field:xds_servers error:field not present]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, TopFieldsWrongTypes) {
const char* json_str =
"{"
" \"xds_servers\":1,"
" \"node\":1,"
" \"server_listener_resource_name_template\":1,"
" \"certificate_providers\":1"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(
bootstrap.status().message(),
"errors validating JSON: ["
"field:certificate_providers error:is not an object; "
"field:node error:is not an object; "
"field:server_listener_resource_name_template error:is not a string; "
"field:xds_servers error:is not an array]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, XdsServerMissingFields) {
const char* json_str =
"{"
" \"xds_servers\":[{}]"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:xds_servers[0].channel_creds error:field not present; "
"field:xds_servers[0].server_uri error:field not present]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, XdsServerUriAndCredsWrongTypes) {
const char* json_str =
"{"
" \"xds_servers\":["
" {"
" \"server_uri\":1,"
" \"channel_creds\":1"
" }"
" ]"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:xds_servers[0].channel_creds error:is not an array; "
"field:xds_servers[0].server_uri error:is not a string]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, ChannelCredsFieldsWrongTypes) {
const char* json_str =
"{"
" \"xds_servers\":["
" {"
" \"server_uri\":\"foo\","
" \"channel_creds\":["
" {"
" \"type\":0,"
" \"config\":1"
" }"
" ]"
" }"
" ]"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(
bootstrap.status().message(),
"errors validating JSON: ["
"field:xds_servers[0].channel_creds[0].config error:is not an object; "
"field:xds_servers[0].channel_creds[0].type error:is not a string]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, NodeFieldsWrongTypes) {
const char* json_str =
"{"
" \"node\":{"
" \"id\":0,"
" \"cluster\":0,"
" \"locality\":0,"
" \"metadata\":0"
" }"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:node.cluster error:is not a string; "
"field:node.id error:is not a string; "
"field:node.locality error:is not an object; "
"field:node.metadata error:is not an object; "
"field:xds_servers error:field not present]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, LocalityFieldsWrongType) {
const char* json_str =
"{"
" \"node\":{"
" \"locality\":{"
" \"region\":0,"
" \"zone\":0,"
" \"sub_zone\":0"
" }"
" }"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:node.locality.region error:is not a string; "
"field:node.locality.sub_zone error:is not a string; "
"field:node.locality.zone error:is not a string; "
"field:xds_servers error:field not present]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, CertificateProvidersElementWrongType) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"fake\"}]"
" }"
" ],"
" \"certificate_providers\": {"
" \"plugin\":1"
" }"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:certificate_providers[\"plugin\"] error:is not an object]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, CertificateProvidersPluginNameWrongType) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"fake\"}]"
" }"
" ],"
" \"certificate_providers\": {"
" \"plugin\": {"
" \"plugin_name\":1"
" }"
" }"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:certificate_providers[\"plugin\"].plugin_name error:"
"is not a string]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, CertificateProvidersUnrecognizedPluginName) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"fake\"}]"
" }"
" ],"
" \"certificate_providers\": {"
" \"plugin\": {"
" \"plugin_name\":\"unknown\""
" }"
" }"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:certificate_providers[\"plugin\"].plugin_name error:"
"Unrecognized plugin name: unknown]")
<< bootstrap.status();
}
TEST(XdsBootstrapTest, AuthorityXdsServerInvalidResourceTemplate) {
gpr_setenv("GRPC_EXPERIMENTAL_XDS_FEDERATION", "true");
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"fake\"}]"
" }"
" ],"
" \"authorities\": {"
" \"xds.example.com\": {"
" \"client_listener_resource_name_template\": "
"\"xds://xds.example.com/envoy.config.listener.v3.Listener/grpc/server/"
"%s\","
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///xds_server\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\""
" }"
" ],"
" \"server_features\": [\"xds_v3\"]"
" }"
" ]"
" }"
" }"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(bootstrap.status().message(),
"errors validating JSON: ["
"field:authorities[\"xds.example.com\"]"
".client_listener_resource_name_template error:"
"field must begin with \"xdstp://xds.example.com/\"]")
<< bootstrap.status();
gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION");
}
TEST(XdsBootstrapTest, AuthorityXdsServerMissingServerUri) {
gpr_setenv("GRPC_EXPERIMENTAL_XDS_FEDERATION", "true");
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"fake\"}]"
" }"
" ],"
" \"authorities\": {"
" \"xds.example.com\": {"
" \"client_listener_resource_name_template\": "
"\"xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/server/"
"%s\","
" \"xds_servers\":[{}]"
" }"
" }"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_EQ(
bootstrap.status().message(),
"errors validating JSON: ["
"field:authorities[\"xds.example.com\"].xds_servers[0].channel_creds "
"error:field not present; "
"field:authorities[\"xds.example.com\"].xds_servers[0].server_uri "
"error:field not present]")
<< bootstrap.status();
gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION");
}
class FakeCertificateProviderFactory : public CertificateProviderFactory {
public:
class Config : public CertificateProviderFactory::Config {
public:
explicit Config(int value) : value_(value) {}
int value() const { return value_; }
const char* name() const override { return "fake"; }
std::string ToString() const override {
return absl::StrFormat(
"{\n"
" value=%d"
"}",
value_);
}
private:
int value_;
};
const char* name() const override { return "fake"; }
RefCountedPtr<CertificateProviderFactory::Config>
CreateCertificateProviderConfig(const Json& config_json,
grpc_error_handle* error) override {
std::vector<grpc_error_handle> error_list;
EXPECT_EQ(config_json.type(), Json::Type::OBJECT);
auto it = config_json.object_value().find("value");
if (it == config_json.object_value().end()) {
return MakeRefCounted<FakeCertificateProviderFactory::Config>(0);
} else if (it->second.type() != Json::Type::NUMBER) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:config field:value not of type number");
} else {
int value = 0;
EXPECT_TRUE(absl::SimpleAtoi(it->second.string_value(), &value));
return MakeRefCounted<FakeCertificateProviderFactory::Config>(value);
}
return nullptr;
}
RefCountedPtr<grpc_tls_certificate_provider> CreateCertificateProvider(
RefCountedPtr<CertificateProviderFactory::Config> /*config*/) override {
return nullptr;
}
};
TEST(XdsBootstrapTest, CertificateProvidersFakePluginParsingError) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"fake\"}]"
" }"
" ],"
" \"certificate_providers\": {"
" \"fake_plugin\": {"
" \"plugin_name\": \"fake\","
" \"config\": {"
" \"value\": \"10\""
" }"
" }"
" }"
"}";
auto bootstrap = GrpcXdsBootstrap::Create(json_str);
EXPECT_THAT(
// Explicit conversion to std::string to work around
// https://github.com/google/googletest/issues/3949.
std::string(bootstrap.status().message()),
::testing::MatchesRegex(
"errors validating JSON: \\["
"field:certificate_providers\\[\"fake_plugin\"\\].config "
"error:UNKNOWN:field:config field:value not of type number.*\\]"))
<< bootstrap.status();
}
TEST(XdsBootstrapTest, CertificateProvidersFakePluginParsingSuccess) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"fake\"}]"
" }"
" ],"
" \"certificate_providers\": {"
" \"fake_plugin\": {"
" \"plugin_name\": \"fake\","
" \"config\": {"
" \"value\": 10"
" }"
" }"
" }"
"}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
auto bootstrap = std::move(*bootstrap_or);
const CertificateProviderStore::PluginDefinition& fake_plugin =
bootstrap->certificate_providers().at("fake_plugin");
ASSERT_EQ(fake_plugin.plugin_name, "fake");
ASSERT_STREQ(fake_plugin.config->name(), "fake");
ASSERT_EQ(static_cast<RefCountedPtr<FakeCertificateProviderFactory::Config>>(
fake_plugin.config)
->value(),
10);
}
TEST(XdsBootstrapTest, CertificateProvidersFakePluginEmptyConfig) {
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": [{\"type\": \"fake\"}]"
" }"
" ],"
" \"certificate_providers\": {"
" \"fake_plugin\": {"
" \"plugin_name\": \"fake\""
" }"
" }"
"}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
auto bootstrap = std::move(*bootstrap_or);
const CertificateProviderStore::PluginDefinition& fake_plugin =
bootstrap->certificate_providers().at("fake_plugin");
ASSERT_EQ(fake_plugin.plugin_name, "fake");
ASSERT_STREQ(fake_plugin.config->name(), "fake");
ASSERT_EQ(static_cast<RefCountedPtr<FakeCertificateProviderFactory::Config>>(
fake_plugin.config)
->value(),
0);
}
TEST(XdsBootstrapTest, XdsServerToJsonAndParse) {
gpr_setenv("GRPC_EXPERIMENTAL_XDS_FEDERATION", "true");
const char* json_str =
" {"
" \"server_uri\": \"fake:///lb\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\","
" \"ignore\": 0"
" }"
" ],"
" \"ignore\": 0"
" }";
auto json = Json::Parse(json_str);
ASSERT_TRUE(json.ok()) << json.status();
auto xds_server = LoadFromJson<GrpcXdsBootstrap::GrpcXdsServer>(*json);
ASSERT_TRUE(xds_server.ok()) << xds_server.status();
Json output = xds_server->ToJson();
auto output_xds_server =
LoadFromJson<GrpcXdsBootstrap::GrpcXdsServer>(output);
ASSERT_TRUE(output_xds_server.ok()) << output_xds_server.status();
EXPECT_EQ(*xds_server, *output_xds_server);
gpr_unsetenv("GRPC_EXPERIMENTAL_XDS_FEDERATION");
}
} // namespace
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc::testing::TestEnvironment env(&argc, argv);
grpc_core::CoreConfiguration::RegisterBuilder(
[](grpc_core::CoreConfiguration::Builder* builder) {
builder->certificate_provider_registry()
->RegisterCertificateProviderFactory(
absl::make_unique<
grpc_core::testing::FakeCertificateProviderFactory>());
});
grpc_init();
int ret = RUN_ALL_TESTS();
grpc_shutdown();
return ret;
}