@@ -150,6 +150,28 @@ def test_ipaddr_info_no_inet_pton(self, m_socket):
150150 socket .SOCK_STREAM ,
151151 socket .IPPROTO_TCP ))
152152
153+ def test_interleave_addrinfos (self ):
154+ SIX_A = (socket .AF_INET6 , 0 , 0 , '' , ('2001:db8::1' , 1 ))
155+ SIX_B = (socket .AF_INET6 , 0 , 0 , '' , ('2001:db8::2' , 2 ))
156+ SIX_C = (socket .AF_INET6 , 0 , 0 , '' , ('2001:db8::3' , 3 ))
157+ SIX_D = (socket .AF_INET6 , 0 , 0 , '' , ('2001:db8::4' , 4 ))
158+ FOUR_A = (socket .AF_INET , 0 , 0 , '' , ('192.0.2.1' , 5 ))
159+ FOUR_B = (socket .AF_INET , 0 , 0 , '' , ('192.0.2.2' , 6 ))
160+ FOUR_C = (socket .AF_INET , 0 , 0 , '' , ('192.0.2.3' , 7 ))
161+ FOUR_D = (socket .AF_INET , 0 , 0 , '' , ('192.0.2.4' , 8 ))
162+
163+ addrinfos = [SIX_A , SIX_B , SIX_C , SIX_D , FOUR_A , FOUR_B , FOUR_C , FOUR_D ]
164+ expected = [SIX_A , FOUR_A , SIX_B , FOUR_B , SIX_C , FOUR_C , SIX_D , FOUR_D ]
165+
166+ self .assertEqual (expected , base_events ._interleave_addrinfos (addrinfos ))
167+
168+ expected_fafc_2 = [SIX_A , SIX_B , FOUR_A , SIX_C , FOUR_B , SIX_D , FOUR_C , FOUR_D ]
169+ self .assertEqual (
170+ expected_fafc_2 ,
171+ base_events ._interleave_addrinfos (addrinfos , first_address_family_count = 2 ),
172+ )
173+
174+
153175
154176class BaseEventLoopTests (test_utils .TestCase ):
155177
@@ -1053,6 +1075,71 @@ def test_asyncgen_finalization_by_gc_in_other_thread(self):
10531075 test_utils .run_briefly (self .loop )
10541076 self .assertTrue (status ['finalized' ])
10551077
1078+ @unittest .skipUnless (socket_helper .IPV6_ENABLED , 'no IPv6 support' )
1079+ @patch_socket
1080+ def test_create_connection_happy_eyeballs (self , m_socket ):
1081+
1082+ class MyProto (asyncio .Protocol ):
1083+ pass
1084+
1085+ async def getaddrinfo (* args , ** kw ):
1086+ return [(socket .AF_INET6 , 0 , 0 , '' , ('2001:db8::1' , 1 )),
1087+ (socket .AF_INET , 0 , 0 , '' , ('192.0.2.1' , 5 ))]
1088+
1089+ async def sock_connect (sock , address ):
1090+ if address [0 ] == '2001:db8::1' :
1091+ await asyncio .sleep (1 )
1092+ sock .connect (address )
1093+
1094+ loop = asyncio .new_event_loop ()
1095+ loop ._add_writer = mock .Mock ()
1096+ loop ._add_writer = mock .Mock ()
1097+ loop ._add_reader = mock .Mock ()
1098+ loop .getaddrinfo = getaddrinfo
1099+ loop .sock_connect = sock_connect
1100+
1101+ coro = loop .create_connection (MyProto , 'example.com' , 80 , happy_eyeballs_delay = 0.3 )
1102+ transport , protocol = loop .run_until_complete (coro )
1103+ try :
1104+ sock = transport ._sock
1105+ sock .connect .assert_called_with (('192.0.2.1' , 5 ))
1106+ finally :
1107+ transport .close ()
1108+ test_utils .run_briefly (loop ) # allow transport to close
1109+ loop .close ()
1110+
1111+ @patch_socket
1112+ def test_create_connection_happy_eyeballs_ipv4_only (self , m_socket ):
1113+
1114+ class MyProto (asyncio .Protocol ):
1115+ pass
1116+
1117+ async def getaddrinfo (* args , ** kw ):
1118+ return [(socket .AF_INET , 0 , 0 , '' , ('192.0.2.1' , 5 )),
1119+ (socket .AF_INET , 0 , 0 , '' , ('192.0.2.2' , 6 ))]
1120+
1121+ async def sock_connect (sock , address ):
1122+ if address [0 ] == '192.0.2.1' :
1123+ await asyncio .sleep (1 )
1124+ sock .connect (address )
1125+
1126+ loop = asyncio .new_event_loop ()
1127+ loop ._add_writer = mock .Mock ()
1128+ loop ._add_writer = mock .Mock ()
1129+ loop ._add_reader = mock .Mock ()
1130+ loop .getaddrinfo = getaddrinfo
1131+ loop .sock_connect = sock_connect
1132+
1133+ coro = loop .create_connection (MyProto , 'example.com' , 80 , happy_eyeballs_delay = 0.3 )
1134+ transport , protocol = loop .run_until_complete (coro )
1135+ try :
1136+ sock = transport ._sock
1137+ sock .connect .assert_called_with (('192.0.2.2' , 6 ))
1138+ finally :
1139+ transport .close ()
1140+ test_utils .run_briefly (loop ) # allow transport to close
1141+ loop .close ()
1142+
10561143
10571144class MyProto (asyncio .Protocol ):
10581145 done = None
0 commit comments