SelectiveTransportExecutorTest.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. <?php
  2. namespace React\Tests\Dns\Query;
  3. use React\Dns\Model\Message;
  4. use React\Dns\Query\Query;
  5. use React\Dns\Query\SelectiveTransportExecutor;
  6. use React\Promise\Deferred;
  7. use React\Promise\Promise;
  8. use React\Tests\Dns\TestCase;
  9. class SelectiveTransportExecutorTest extends TestCase
  10. {
  11. private $datagram;
  12. private $stream;
  13. private $executor;
  14. /**
  15. * @before
  16. */
  17. public function setUpMocks()
  18. {
  19. $this->datagram = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock();
  20. $this->stream = $this->getMockBuilder('React\Dns\Query\ExecutorInterface')->getMock();
  21. $this->executor = new SelectiveTransportExecutor($this->datagram, $this->stream);
  22. }
  23. public function testQueryResolvesWhenDatagramTransportResolvesWithoutUsingStreamTransport()
  24. {
  25. $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
  26. $response = new Message();
  27. $this->datagram
  28. ->expects($this->once())
  29. ->method('query')
  30. ->with($query)
  31. ->willReturn(\React\Promise\resolve($response));
  32. $this->stream
  33. ->expects($this->never())
  34. ->method('query');
  35. $promise = $this->executor->query($query);
  36. $promise->then($this->expectCallableOnceWith($response));
  37. }
  38. public function testQueryResolvesWhenStreamTransportResolvesAfterDatagramTransportRejectsWithSizeError()
  39. {
  40. $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
  41. $response = new Message();
  42. $this->datagram
  43. ->expects($this->once())
  44. ->method('query')
  45. ->with($query)
  46. ->willReturn(\React\Promise\reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90)));
  47. $this->stream
  48. ->expects($this->once())
  49. ->method('query')
  50. ->with($query)
  51. ->willReturn(\React\Promise\resolve($response));
  52. $promise = $this->executor->query($query);
  53. $promise->then($this->expectCallableOnceWith($response));
  54. }
  55. public function testQueryRejectsWhenDatagramTransportRejectsWithRuntimeExceptionWithoutUsingStreamTransport()
  56. {
  57. $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
  58. $this->datagram
  59. ->expects($this->once())
  60. ->method('query')
  61. ->with($query)
  62. ->willReturn(\React\Promise\reject(new \RuntimeException()));
  63. $this->stream
  64. ->expects($this->never())
  65. ->method('query');
  66. $promise = $this->executor->query($query);
  67. $promise->then(null, $this->expectCallableOnce());
  68. }
  69. public function testQueryRejectsWhenStreamTransportRejectsAfterDatagramTransportRejectsWithSizeError()
  70. {
  71. $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
  72. $this->datagram
  73. ->expects($this->once())
  74. ->method('query')
  75. ->with($query)
  76. ->willReturn(\React\Promise\reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90)));
  77. $this->stream
  78. ->expects($this->once())
  79. ->method('query')
  80. ->with($query)
  81. ->willReturn(\React\Promise\reject(new \RuntimeException()));
  82. $promise = $this->executor->query($query);
  83. $promise->then(null, $this->expectCallableOnce());
  84. }
  85. public function testCancelPromiseWillCancelPromiseFromDatagramExecutor()
  86. {
  87. $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
  88. $this->datagram
  89. ->expects($this->once())
  90. ->method('query')
  91. ->with($query)
  92. ->willReturn(new Promise(function () {}, $this->expectCallableOnce()));
  93. $promise = $this->executor->query($query);
  94. $promise->cancel();
  95. }
  96. public function testCancelPromiseWillCancelPromiseFromStreamExecutorWhenDatagramExecutorRejectedWithTruncatedResponse()
  97. {
  98. $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
  99. $deferred = new Deferred();
  100. $this->datagram
  101. ->expects($this->once())
  102. ->method('query')
  103. ->with($query)
  104. ->willReturn($deferred->promise());
  105. $this->stream
  106. ->expects($this->once())
  107. ->method('query')
  108. ->with($query)
  109. ->willReturn(new Promise(function () {}, $this->expectCallableOnce()));
  110. $promise = $this->executor->query($query);
  111. $deferred->reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90));
  112. $promise->cancel();
  113. }
  114. public function testCancelPromiseShouldNotCreateAnyGarbageReferences()
  115. {
  116. if (class_exists('React\Promise\When')) {
  117. $this->markTestSkipped('Not supported on legacy Promise v1 API');
  118. }
  119. $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
  120. $this->datagram
  121. ->expects($this->once())
  122. ->method('query')
  123. ->with($query)
  124. ->willReturn(new Promise(function () {}, function () {
  125. throw new \RuntimeException('Cancelled');
  126. }));
  127. while (gc_collect_cycles()) {
  128. // collect all garbage cycles
  129. }
  130. $promise = $this->executor->query($query);
  131. $promise->cancel();
  132. unset($promise);
  133. $this->assertEquals(0, gc_collect_cycles());
  134. }
  135. public function testCancelPromiseAfterTruncatedResponseShouldNotCreateAnyGarbageReferences()
  136. {
  137. if (class_exists('React\Promise\When')) {
  138. $this->markTestSkipped('Not supported on legacy Promise v1 API');
  139. }
  140. $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
  141. $deferred = new Deferred();
  142. $this->datagram
  143. ->expects($this->once())
  144. ->method('query')
  145. ->with($query)
  146. ->willReturn($deferred->promise());
  147. $this->stream
  148. ->expects($this->once())
  149. ->method('query')
  150. ->with($query)
  151. ->willReturn(new Promise(function () {}, function () {
  152. throw new \RuntimeException('Cancelled');
  153. }));
  154. while (gc_collect_cycles()) {
  155. // collect all garbage cycles
  156. }
  157. $promise = $this->executor->query($query);
  158. $deferred->reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90));
  159. $promise->cancel();
  160. unset($promise);
  161. $this->assertEquals(0, gc_collect_cycles());
  162. }
  163. public function testRejectedPromiseAfterTruncatedResponseShouldNotCreateAnyGarbageReferences()
  164. {
  165. $query = new Query('igor.io', Message::TYPE_A, Message::CLASS_IN);
  166. $this->datagram
  167. ->expects($this->once())
  168. ->method('query')
  169. ->with($query)
  170. ->willReturn(\React\Promise\reject(new \RuntimeException('', defined('SOCKET_EMSGSIZE') ? SOCKET_EMSGSIZE : 90)));
  171. $this->stream
  172. ->expects($this->once())
  173. ->method('query')
  174. ->with($query)
  175. ->willReturn(\React\Promise\reject(new \RuntimeException()));
  176. while (gc_collect_cycles()) {
  177. // collect all garbage cycles
  178. }
  179. $promise = $this->executor->query($query);
  180. $promise->then(null, $this->expectCallableOnce()); // avoid reporting unhandled rejection
  181. unset($promise);
  182. $this->assertEquals(0, gc_collect_cycles());
  183. }
  184. }