@ -32,11 +32,10 @@
# include "absl/container/inlined_vector.h"
# include "absl/status/statusor.h"
# include "absl/strings/str_format.h"
# include "absl/synchronization/notification.h"
# include "gtest/gtest.h"
# include <grpc/grpc.h>
# include <grpc/support/log.h>
# include <grpc/support/sync.h>
# include "src/core/lib/address_utils/parse_address.h"
# include "src/core/lib/channel/channel_args.h"
@ -54,180 +53,175 @@
# include "src/core/lib/uri/uri_parser.h"
# include "test/core/util/test_config.h"
class ResultHandler : public grpc_core : : Resolver : : ResultHandler {
public :
void SetExpectedAndEvent ( grpc_core : : Resolver : : Result expected ,
gpr_event * ev ) {
grpc_core : : MutexLock lock ( & mu_ ) ;
ASSERT_EQ ( ev_ , nullptr ) ;
expected_ = std : : move ( expected ) ;
ev_ = ev ;
}
namespace grpc_core {
namespace testing {
void ReportResult ( grpc_core : : Resolver : : Result actual ) override {
grpc_core : : MutexLock lock ( & mu_ ) ;
ASSERT_NE ( ev_ , nullptr ) ;
// We only check the addresses, because that's the only thing
// explicitly set by the test via
// FakeResolverResponseGenerator::SetResponse().
ASSERT_TRUE ( actual . addresses . ok ( ) ) ;
ASSERT_EQ ( actual . addresses - > size ( ) , expected_ . addresses - > size ( ) ) ;
for ( size_t i = 0 ; i < expected_ . addresses - > size ( ) ; + + i ) {
ASSERT_EQ ( ( * actual . addresses ) [ i ] , ( * expected_ . addresses ) [ i ] ) ;
class FakeResolverTest : public : : testing : : Test {
protected :
class ResultHandler : public Resolver : : ResultHandler {
public :
void SetExpectedAndNotification ( Resolver : : Result expected ,
absl : : Notification * notification ) {
MutexLock lock ( & mu_ ) ;
ASSERT_EQ ( notification_ , nullptr ) ;
expected_ = std : : move ( expected ) ;
notification_ = notification ;
}
gpr_event_set ( ev_ , reinterpret_cast < void * > ( 1 ) ) ;
ev_ = nullptr ;
}
private :
grpc_core : : Mutex mu_ ;
grpc_core : : Resolver : : Result expected_ ABSL_GUARDED_BY ( mu_ ) ;
gpr_event * ev_ ABSL_GUARDED_BY ( mu_ ) = nullptr ;
} ;
void ReportResult ( Resolver : : Result actual ) override {
MutexLock lock ( & mu_ ) ;
ASSERT_NE ( notification_ , nullptr ) ;
// TODO(roth): Check fields other than just the addresses.
// Note: No good way to compare result_health_callback.
ASSERT_TRUE ( actual . addresses . ok ( ) ) ;
ASSERT_EQ ( actual . addresses - > size ( ) , expected_ . addresses - > size ( ) ) ;
for ( size_t i = 0 ; i < expected_ . addresses - > size ( ) ; + + i ) {
ASSERT_EQ ( ( * actual . addresses ) [ i ] , ( * expected_ . addresses ) [ i ] ) ;
}
notification_ - > Notify ( ) ;
notification_ = nullptr ;
}
static grpc_core : : OrphanablePtr < grpc_core : : Resolver > build_fake_resolver (
std : : shared_ptr < grpc_core : : WorkSerializer > work_serializer ,
grpc_core : : FakeResolverResponseGenerator * response_generator ,
std : : unique_ptr < grpc_core : : Resolver : : ResultHandler > result_handler ) {
grpc_core : : ResolverFactory * factory = grpc_core : : CoreConfiguration : : Get ( )
. resolver_registry ( )
. LookupResolverFactory ( " fake " ) ;
grpc_arg generator_arg =
grpc_core : : FakeResolverResponseGenerator : : MakeChannelArg (
response_generator ) ;
grpc_channel_args channel_args = { 1 , & generator_arg } ;
grpc_core : : ResolverArgs args ;
args . args = grpc_core : : ChannelArgs : : FromC ( & channel_args ) ;
args . work_serializer = std : : move ( work_serializer ) ;
args . result_handler = std : : move ( result_handler ) ;
grpc_core : : OrphanablePtr < grpc_core : : Resolver > resolver =
factory - > CreateResolver ( std : : move ( args ) ) ;
return resolver ;
}
private :
Mutex mu_ ;
Resolver : : Result expected_ ABSL_GUARDED_BY ( mu_ ) ;
absl : : Notification * notification_ ABSL_GUARDED_BY ( mu_ ) = nullptr ;
} ;
// Create a new resolution containing 2 addresses.
static grpc_core : : Resolver : : Result create_new_resolver_result ( ) {
static size_t test_counter = 0 ;
const size_t num_addresses = 2 ;
// Create address list.
grpc_core : : EndpointAddressesList addresses ;
for ( size_t i = 0 ; i < num_addresses ; + + i ) {
std : : string uri_string = absl : : StrFormat ( " ipv4:127.0.0.1:100% " PRIuPTR ,
test_counter * num_addresses + i ) ;
absl : : StatusOr < grpc_core : : URI > uri = grpc_core : : URI : : Parse ( uri_string ) ;
EXPECT_TRUE ( uri . ok ( ) ) ;
grpc_resolved_address address ;
EXPECT_TRUE ( grpc_parse_uri ( * uri , & address ) ) ;
absl : : InlinedVector < grpc_arg , 2 > args_to_add ;
addresses . emplace_back ( address , grpc_core : : ChannelArgs ( ) ) ;
static OrphanablePtr < Resolver > BuildFakeResolver (
std : : shared_ptr < WorkSerializer > work_serializer ,
RefCountedPtr < FakeResolverResponseGenerator > response_generator ,
std : : unique_ptr < Resolver : : ResultHandler > result_handler ) {
ResolverFactory * factory =
CoreConfiguration : : Get ( ) . resolver_registry ( ) . LookupResolverFactory (
" fake " ) ;
ResolverArgs args ;
args . args = ChannelArgs ( ) . SetObject ( std : : move ( response_generator ) ) ;
args . work_serializer = std : : move ( work_serializer ) ;
args . result_handler = std : : move ( result_handler ) ;
return factory - > CreateResolver ( std : : move ( args ) ) ;
}
+ + test_counter ;
grpc_core : : Resolver : : Result result ;
result . addresses = std : : move ( addresses ) ;
return result ;
}
TEST ( FakeResolverTest , FakeResolver ) {
grpc_core : : ExecCtx exec_ctx ;
std : : shared_ptr < grpc_core : : WorkSerializer > work_serializer =
std : : make_shared < grpc_core : : WorkSerializer > (
grpc_event_engine : : experimental : : GetDefaultEventEngine ( ) ) ;
auto synchronously = [ work_serializer ] ( std : : function < void ( ) > do_this_thing ) {
grpc_core : : Notification notification ;
work_serializer - > Run (
[ do_this_thing = std : : move ( do_this_thing ) , & notification ] ( ) mutable {
do_this_thing ( ) ;
// Create a new resolution containing 2 addresses.
static Resolver : : Result CreateResolverResult ( ) {
static size_t test_counter = 0 ;
const size_t num_addresses = 2 ;
// Create address list.
EndpointAddressesList addresses ;
for ( size_t i = 0 ; i < num_addresses ; + + i ) {
std : : string uri_string = absl : : StrFormat (
" ipv4:127.0.0.1:100% " PRIuPTR , test_counter * num_addresses + i ) ;
absl : : StatusOr < URI > uri = URI : : Parse ( uri_string ) ;
EXPECT_TRUE ( uri . ok ( ) ) ;
grpc_resolved_address address ;
EXPECT_TRUE ( grpc_parse_uri ( * uri , & address ) ) ;
absl : : InlinedVector < grpc_arg , 2 > args_to_add ;
addresses . emplace_back ( address , ChannelArgs ( ) ) ;
}
+ + test_counter ;
Resolver : : Result result ;
result . addresses = std : : move ( addresses ) ;
return result ;
}
OrphanablePtr < Resolver > CreateResolver ( ) {
result_handler_ = new ResultHandler ( ) ;
return BuildFakeResolver (
work_serializer_ , response_generator_ ,
std : : unique_ptr < Resolver : : ResultHandler > ( result_handler_ ) ) ;
}
void RunSynchronously ( std : : function < void ( ) > callback ) {
Notification notification ;
work_serializer_ - > Run (
[ callback = std : : move ( callback ) , & notification ] ( ) {
callback ( ) ;
notification . Notify ( ) ;
} ,
DEBUG_LOCATION ) ;
notification . WaitForNotification ( ) ;
} ;
}
ExecCtx exec_ctx_ ;
std : : shared_ptr < WorkSerializer > work_serializer_ =
std : : make_shared < WorkSerializer > (
grpc_event_engine : : experimental : : GetDefaultEventEngine ( ) ) ;
RefCountedPtr < FakeResolverResponseGenerator > response_generator_ =
MakeRefCounted < FakeResolverResponseGenerator > ( ) ;
ResultHandler * result_handler_ = nullptr ;
} ;
TEST_F ( FakeResolverTest , WaitForResolverSet ) {
EXPECT_FALSE ( response_generator_ - > WaitForResolverSet ( absl : : Milliseconds ( 1 ) ) ) ;
auto resolver = CreateResolver ( ) ;
ASSERT_NE ( resolver , nullptr ) ;
EXPECT_TRUE ( response_generator_ - > WaitForResolverSet ( absl : : Milliseconds ( 1 ) ) ) ;
}
TEST_F ( FakeResolverTest , ReturnResultBeforeResolverCreated ) {
// Return result via response generator.
Resolver : : Result result = CreateResolverResult ( ) ;
response_generator_ - > SetResponseAsync ( result ) ;
// Create and start resolver.
auto resolver = CreateResolver ( ) ;
ASSERT_NE ( resolver , nullptr ) ;
absl : : Notification notification ;
result_handler_ - > SetExpectedAndNotification ( std : : move ( result ) , & notification ) ;
RunSynchronously ( [ resolver = resolver . get ( ) ] { resolver - > StartLocked ( ) ; } ) ;
// Expect result.
ASSERT_TRUE ( notification . WaitForNotificationWithTimeout (
absl : : Seconds ( 5 * grpc_test_slowdown_factor ( ) ) ) ) ;
}
TEST_F ( FakeResolverTest , ReturnResultBeforeResolverStarted ) {
// Create resolver.
ResultHandler * result_handler = new ResultHandler ( ) ;
grpc_core : : RefCountedPtr < grpc_core : : FakeResolverResponseGenerator >
response_generator =
grpc_core : : MakeRefCounted < grpc_core : : FakeResolverResponseGenerator > ( ) ;
grpc_core : : OrphanablePtr < grpc_core : : Resolver > resolver = build_fake_resolver (
work_serializer , response_generator . get ( ) ,
std : : unique_ptr < grpc_core : : Resolver : : ResultHandler > ( result_handler ) ) ;
ASSERT_NE ( resolver . get ( ) , nullptr ) ;
synchronously ( [ resolver = resolver . get ( ) ] { resolver - > StartLocked ( ) ; } ) ;
// Test 1: normal resolution.
// next_results != NULL, reresolution_results == NULL.
// Expected response is next_results.
gpr_log ( GPR_INFO , " TEST 1 " ) ;
grpc_core : : Resolver : : Result result = create_new_resolver_result ( ) ;
gpr_event ev1 ;
gpr_event_init ( & ev1 ) ;
result_handler - > SetExpectedAndEvent ( result , & ev1 ) ;
response_generator - > SetResponseSynchronously ( std : : move ( result ) ) ;
grpc_core : : ExecCtx : : Get ( ) - > Flush ( ) ;
ASSERT_NE ( gpr_event_wait ( & ev1 , grpc_timeout_seconds_to_deadline ( 5 ) ) , nullptr ) ;
// Test 2: update resolution.
// next_results != NULL, reresolution_results == NULL.
// Expected response is next_results.
gpr_log ( GPR_INFO , " TEST 2 " ) ;
result = create_new_resolver_result ( ) ;
gpr_event ev2 ;
gpr_event_init ( & ev2 ) ;
result_handler - > SetExpectedAndEvent ( result , & ev2 ) ;
response_generator - > SetResponseSynchronously ( std : : move ( result ) ) ;
grpc_core : : ExecCtx : : Get ( ) - > Flush ( ) ;
ASSERT_NE ( gpr_event_wait ( & ev2 , grpc_timeout_seconds_to_deadline ( 5 ) ) , nullptr ) ;
// Test 3: normal re-resolution.
// next_results == NULL, reresolution_results != NULL.
// Expected response is reresolution_results.
gpr_log ( GPR_INFO , " TEST 3 " ) ;
grpc_core : : Resolver : : Result reresolution_result =
create_new_resolver_result ( ) ;
gpr_event ev3 ;
gpr_event_init ( & ev3 ) ;
result_handler - > SetExpectedAndEvent ( reresolution_result , & ev3 ) ;
// Set reresolution_results.
// No result will be returned until re-resolution is requested.
response_generator - > SetReresolutionResponseSynchronously ( reresolution_result ) ;
grpc_core : : ExecCtx : : Get ( ) - > Flush ( ) ;
// Trigger a re-resolution.
synchronously (
[ resolver = resolver . get ( ) ] { resolver - > RequestReresolutionLocked ( ) ; } ) ;
grpc_core : : ExecCtx : : Get ( ) - > Flush ( ) ;
ASSERT_NE ( gpr_event_wait ( & ev3 , grpc_timeout_seconds_to_deadline ( 5 ) ) , nullptr ) ;
// Test 4: repeat re-resolution.
// next_results == NULL, reresolution_results != NULL.
// Expected response is reresolution_results.
gpr_log ( GPR_INFO , " TEST 4 " ) ;
gpr_event ev4 ;
gpr_event_init ( & ev4 ) ;
result_handler - > SetExpectedAndEvent ( std : : move ( reresolution_result ) , & ev4 ) ;
// Trigger a re-resolution.
synchronously (
auto resolver = CreateResolver ( ) ;
ASSERT_NE ( resolver , nullptr ) ;
Resolver : : Result result = CreateResolverResult ( ) ;
absl : : Notification notification ;
result_handler_ - > SetExpectedAndNotification ( result , & notification ) ;
// Return result via response generator.
response_generator_ - > SetResponseAsync ( std : : move ( result ) ) ;
// Start resolver.
RunSynchronously ( [ resolver = resolver . get ( ) ] { resolver - > StartLocked ( ) ; } ) ;
// Expect result.
ASSERT_TRUE ( notification . WaitForNotificationWithTimeout (
absl : : Seconds ( 5 * grpc_test_slowdown_factor ( ) ) ) ) ;
}
TEST_F ( FakeResolverTest , ReturnResult ) {
// Create and start resolver.
auto resolver = CreateResolver ( ) ;
ASSERT_NE ( resolver , nullptr ) ;
RunSynchronously ( [ resolver = resolver . get ( ) ] { resolver - > StartLocked ( ) ; } ) ;
Resolver : : Result result = CreateResolverResult ( ) ;
absl : : Notification notification ;
result_handler_ - > SetExpectedAndNotification ( result , & notification ) ;
// Return result via response generator.
response_generator_ - > SetResponseAsync ( std : : move ( result ) ) ;
// Expect result.
ASSERT_TRUE ( notification . WaitForNotificationWithTimeout (
absl : : Seconds ( 5 * grpc_test_slowdown_factor ( ) ) ) ) ;
}
TEST_F ( FakeResolverTest , WaitForReresolutionRequest ) {
// Create and start resolver.
auto resolver = CreateResolver ( ) ;
ASSERT_NE ( resolver , nullptr ) ;
RunSynchronously ( [ resolver = resolver . get ( ) ] { resolver - > StartLocked ( ) ; } ) ;
// No re-resolution requested yet.
EXPECT_FALSE (
response_generator_ - > WaitForReresolutionRequest ( absl : : Milliseconds ( 1 ) ) ) ;
// Request re-resolution, then try again.
RunSynchronously (
[ resolver = resolver . get ( ) ] { resolver - > RequestReresolutionLocked ( ) ; } ) ;
grpc_core : : ExecCtx : : Get ( ) - > Flush ( ) ;
ASSERT_NE ( gpr_event_wait ( & ev4 , grpc_timeout_seconds_to_deadline ( 5 ) ) , nullptr ) ;
// Test 5: normal resolution.
// next_results != NULL, reresolution_results != NULL.
// Expected response is next_results.
gpr_log ( GPR_INFO , " TEST 5 " ) ;
result = create_new_resolver_result ( ) ;
gpr_event ev5 ;
gpr_event_init ( & ev5 ) ;
result_handler - > SetExpectedAndEvent ( result , & ev5 ) ;
response_generator - > SetResponseSynchronously ( std : : move ( result ) ) ;
grpc_core : : ExecCtx : : Get ( ) - > Flush ( ) ;
ASSERT_NE ( gpr_event_wait ( & ev5 , grpc_timeout_seconds_to_deadline ( 5 ) ) , nullptr ) ;
// Test 6: no-op.
// Requesting a new resolution without setting the response shouldn't trigger
// the resolution callback.
gpr_log ( GPR_INFO , " TEST 6 " ) ;
gpr_event ev6 ;
gpr_event_init ( & ev6 ) ;
result_handler - > SetExpectedAndEvent ( grpc_core : : Resolver : : Result ( ) , & ev6 ) ;
ASSERT_EQ ( gpr_event_wait ( & ev6 , grpc_timeout_milliseconds_to_deadline ( 100 ) ) ,
nullptr ) ;
// Clean up.
resolver . reset ( ) ;
EXPECT_TRUE (
response_generator_ - > WaitForReresolutionRequest ( absl : : Milliseconds ( 1 ) ) ) ;
}
} // namespace testing
} // namespace grpc_core
int main ( int argc , char * * argv ) {
grpc : : testing : : TestEnvironment env ( & argc , argv ) ;
: : testing : : InitGoogleTest ( & argc , argv ) ;