@@ -65,6 +65,42 @@ public function testConnectWillRejectWhenBothDnsLookupsReject()
6565 $ this ->assertEquals ('Connection to tcp://reactphp.org:80 failed during DNS lookup: DNS lookup error ' , $ exception ->getMessage ());
6666 }
6767
68+ public function testConnectWillRejectWhenBothDnsLookupsRejectWithDifferentMessages ()
69+ {
70+ $ loop = $ this ->getMockBuilder ('React\EventLoop\LoopInterface ' )->getMock ();
71+ $ loop ->expects ($ this ->never ())->method ('addTimer ' );
72+
73+ $ connector = $ this ->getMockBuilder ('React\Socket\ConnectorInterface ' )->getMock ();
74+ $ connector ->expects ($ this ->never ())->method ('connect ' );
75+
76+ $ deferred = new Deferred ();
77+ $ resolver = $ this ->getMockBuilder ('React\Dns\Resolver\ResolverInterface ' )->getMock ();
78+ $ resolver ->expects ($ this ->exactly (2 ))->method ('resolveAll ' )->withConsecutive (
79+ array ('reactphp.org ' , Message::TYPE_AAAA ),
80+ array ('reactphp.org ' , Message::TYPE_A )
81+ )->willReturnOnConsecutiveCalls (
82+ $ deferred ->promise (),
83+ \React \Promise \reject (new \RuntimeException ('DNS4 error ' ))
84+ );
85+
86+ $ uri = 'tcp://reactphp.org:80 ' ;
87+ $ host = 'reactphp.org ' ;
88+ $ parts = parse_url ($ uri );
89+
90+ $ builder = new HappyEyeBallsConnectionBuilder ($ loop , $ connector , $ resolver , $ uri , $ host , $ parts );
91+
92+ $ promise = $ builder ->connect ();
93+ $ deferred ->reject (new \RuntimeException ('DNS6 error ' ));
94+
95+ $ exception = null ;
96+ $ promise ->then (null , function ($ e ) use (&$ exception ) {
97+ $ exception = $ e ;
98+ });
99+
100+ $ this ->assertInstanceOf ('RuntimeException ' , $ exception );
101+ $ this ->assertEquals ('Connection to tcp://reactphp.org:80 failed during DNS lookup. Last error for IPv6: DNS6 error. Previous error for IPv4: DNS4 error ' , $ exception ->getMessage ());
102+ }
103+
68104 public function testConnectWillStartDelayTimerWhenIpv4ResolvesAndIpv6IsPending ()
69105 {
70106 $ loop = $ this ->getMockBuilder ('React\EventLoop\LoopInterface ' )->getMock ();
@@ -364,7 +400,7 @@ public function testConnectWillStartAndCancelResolutionTimerAndStartAttemptTimer
364400 $ deferred ->resolve (array ('::1 ' ));
365401 }
366402
367- public function testConnectWillRejectWhenOnlyTcpConnectionRejectsAndCancelNextAttemptTimerImmediately ()
403+ public function testConnectWillRejectWhenOnlyTcp6ConnectionRejectsAndCancelNextAttemptTimerImmediately ()
368404 {
369405 $ timer = $ this ->getMockBuilder ('React\EventLoop\TimerInterface ' )->getMock ();
370406 $ loop = $ this ->getMockBuilder ('React\EventLoop\LoopInterface ' )->getMock ();
@@ -381,7 +417,81 @@ public function testConnectWillRejectWhenOnlyTcpConnectionRejectsAndCancelNextAt
381417 array ('reactphp.org ' , Message::TYPE_A )
382418 )->willReturnOnConsecutiveCalls (
383419 \React \Promise \resolve (array ('::1 ' )),
384- \React \Promise \reject (new \RuntimeException ('ignored ' ))
420+ \React \Promise \reject (new \RuntimeException ('DNS failed ' ))
421+ );
422+
423+ $ uri = 'tcp://reactphp.org:80 ' ;
424+ $ host = 'reactphp.org ' ;
425+ $ parts = parse_url ($ uri );
426+
427+ $ builder = new HappyEyeBallsConnectionBuilder ($ loop , $ connector , $ resolver , $ uri , $ host , $ parts );
428+
429+ $ promise = $ builder ->connect ();
430+ $ deferred ->reject (new \RuntimeException ('Connection refused ' ));
431+
432+ $ exception = null ;
433+ $ promise ->then (null , function ($ e ) use (&$ exception ) {
434+ $ exception = $ e ;
435+ });
436+
437+ $ this ->assertInstanceOf ('RuntimeException ' , $ exception );
438+ $ this ->assertEquals ('Connection to tcp://reactphp.org:80 failed: Last error for IPv6: Connection refused. Previous error for IPv4: DNS failed ' , $ exception ->getMessage ());
439+ }
440+
441+ public function testConnectWillRejectWhenOnlyTcp4ConnectionRejectsAndWillNeverStartNextAttemptTimer ()
442+ {
443+ $ loop = $ this ->getMockBuilder ('React\EventLoop\LoopInterface ' )->getMock ();
444+ $ loop ->expects ($ this ->never ())->method ('addTimer ' );
445+
446+ $ deferred = new Deferred ();
447+ $ connector = $ this ->getMockBuilder ('React\Socket\ConnectorInterface ' )->getMock ();
448+ $ connector ->expects ($ this ->once ())->method ('connect ' )->with ('tcp://127.0.0.1:80?hostname=reactphp.org ' )->willReturn ($ deferred ->promise ());
449+
450+ $ resolver = $ this ->getMockBuilder ('React\Dns\Resolver\ResolverInterface ' )->getMock ();
451+ $ resolver ->expects ($ this ->exactly (2 ))->method ('resolveAll ' )->withConsecutive (
452+ array ('reactphp.org ' , Message::TYPE_AAAA ),
453+ array ('reactphp.org ' , Message::TYPE_A )
454+ )->willReturnOnConsecutiveCalls (
455+ \React \Promise \reject (new \RuntimeException ('DNS failed ' )),
456+ \React \Promise \resolve (array ('127.0.0.1 ' ))
457+ );
458+
459+ $ uri = 'tcp://reactphp.org:80 ' ;
460+ $ host = 'reactphp.org ' ;
461+ $ parts = parse_url ($ uri );
462+
463+ $ builder = new HappyEyeBallsConnectionBuilder ($ loop , $ connector , $ resolver , $ uri , $ host , $ parts );
464+
465+ $ promise = $ builder ->connect ();
466+ $ deferred ->reject (new \RuntimeException ('Connection refused ' ));
467+
468+ $ exception = null ;
469+ $ promise ->then (null , function ($ e ) use (&$ exception ) {
470+ $ exception = $ e ;
471+ });
472+
473+ $ this ->assertInstanceOf ('RuntimeException ' , $ exception );
474+ $ this ->assertEquals ('Connection to tcp://reactphp.org:80 failed: Last error for IPv4: Connection refused. Previous error for IPv6: DNS failed ' , $ exception ->getMessage ());
475+ }
476+
477+ public function testConnectWillRejectWhenAllConnectionsRejectAndCancelNextAttemptTimerImmediately ()
478+ {
479+ $ timer = $ this ->getMockBuilder ('React\EventLoop\TimerInterface ' )->getMock ();
480+ $ loop = $ this ->getMockBuilder ('React\EventLoop\LoopInterface ' )->getMock ();
481+ $ loop ->expects ($ this ->once ())->method ('addTimer ' )->with (0.1 , $ this ->anything ())->willReturn ($ timer );
482+ $ loop ->expects ($ this ->once ())->method ('cancelTimer ' )->with ($ timer );
483+
484+ $ deferred = new Deferred ();
485+ $ connector = $ this ->getMockBuilder ('React\Socket\ConnectorInterface ' )->getMock ();
486+ $ connector ->expects ($ this ->exactly (2 ))->method ('connect ' )->willReturn ($ deferred ->promise ());
487+
488+ $ resolver = $ this ->getMockBuilder ('React\Dns\Resolver\ResolverInterface ' )->getMock ();
489+ $ resolver ->expects ($ this ->exactly (2 ))->method ('resolveAll ' )->withConsecutive (
490+ array ('reactphp.org ' , Message::TYPE_AAAA ),
491+ array ('reactphp.org ' , Message::TYPE_A )
492+ )->willReturnOnConsecutiveCalls (
493+ \React \Promise \resolve (array ('::1 ' )),
494+ \React \Promise \resolve (array ('127.0.0.1 ' ))
385495 );
386496
387497 $ uri = 'tcp://reactphp.org:80 ' ;
0 commit comments