PipelineContextTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. <?php
  2. /*
  3. * This file is part of the Predis package.
  4. *
  5. * (c) Daniele Alessandri <suppakilla@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Predis\Pipeline;
  11. use \PHPUnit_Framework_TestCase as StandardTestCase;
  12. use Predis\Client;
  13. use Predis\ClientException;
  14. use Predis\Profile\ServerProfile;
  15. /**
  16. *
  17. */
  18. class PipelineContextTest extends StandardTestCase
  19. {
  20. /**
  21. * @group disconnected
  22. */
  23. public function testConstructorWithoutOptions()
  24. {
  25. $client = new Client();
  26. $pipeline = new PipelineContext($client);
  27. $this->assertSame($client, $pipeline->getClient());
  28. $this->assertInstanceOf('Predis\Pipeline\StandardExecutor', $pipeline->getExecutor());
  29. }
  30. /**
  31. * @group disconnected
  32. */
  33. public function testConstructorWithOptions()
  34. {
  35. $client = new Client();
  36. $executor = $this->getMock('Predis\Pipeline\PipelineExecutorInterface');
  37. $pipeline = new PipelineContext($client, array('executor' => $executor));
  38. $this->assertSame($executor, $pipeline->getExecutor());
  39. $executorCbk = function($client, $options) use($executor) { return $executor; };
  40. $pipeline = new PipelineContext($client, array('executor' => $executorCbk));
  41. $this->assertSame($executor, $pipeline->getExecutor());
  42. }
  43. /**
  44. * @group disconnected
  45. */
  46. public function testCallDoesNotSendCommandsWithoutExecute()
  47. {
  48. $executor = $this->getMock('Predis\Pipeline\PipelineExecutorInterface');
  49. $executor->expects($this->never())->method('executor');
  50. $pipeline = new PipelineContext(new Client(), array('executor' => $executor));
  51. $pipeline->echo('one');
  52. $pipeline->echo('two');
  53. $pipeline->echo('three');
  54. }
  55. /**
  56. * @group disconnected
  57. */
  58. public function testCallReturnsPipelineForFluentInterface()
  59. {
  60. $executor = $this->getMock('Predis\Pipeline\PipelineExecutorInterface');
  61. $executor->expects($this->never())->method('executor');
  62. $pipeline = new PipelineContext(new Client(), array('executor' => $executor));
  63. $this->assertSame($pipeline, $pipeline->echo('one'));
  64. $this->assertSame($pipeline, $pipeline->echo('one')->echo('two')->echo('three'));
  65. }
  66. /**
  67. * @group disconnected
  68. */
  69. public function testExecuteCommandDoesNotSendCommandsWithoutExecute()
  70. {
  71. $profile = ServerProfile::getDefault();
  72. $executor = $this->getMock('Predis\Pipeline\PipelineExecutorInterface');
  73. $executor->expects($this->never())->method('executor');
  74. $pipeline = new PipelineContext(new Client(), array('executor' => $executor));
  75. $pipeline->executeCommand($profile->createCommand('echo', array('one')));
  76. $pipeline->executeCommand($profile->createCommand('echo', array('two')));
  77. $pipeline->executeCommand($profile->createCommand('echo', array('three')));
  78. }
  79. /**
  80. * @group disconnected
  81. */
  82. public function testExecuteWithEmptyBuffer()
  83. {
  84. $executor = $this->getMock('Predis\Pipeline\PipelineExecutorInterface');
  85. $executor->expects($this->never())->method('executor');
  86. $pipeline = new PipelineContext(new Client(), array('executor' => $executor));
  87. $this->assertSame(array(), $pipeline->execute());
  88. }
  89. /**
  90. * @group disconnected
  91. */
  92. public function testExecuteWithFilledBuffer()
  93. {
  94. $connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
  95. $connection->expects($this->exactly(3))
  96. ->method('writeCommand');
  97. $connection->expects($this->exactly(3))
  98. ->method('readResponse')
  99. ->will($this->returnCallback($this->getReadCallback()));
  100. $pipeline = new PipelineContext(new Client($connection));
  101. $pipeline->echo('one');
  102. $pipeline->echo('two');
  103. $pipeline->echo('three');
  104. $pipeline->flushPipeline();
  105. $this->assertSame(array('one', 'two', 'three'), $pipeline->execute());
  106. }
  107. /**
  108. * @group disconnected
  109. */
  110. public function testFlushWithFalseArgumentDiscardsBuffer()
  111. {
  112. $executor = $this->getMock('Predis\Pipeline\PipelineExecutorInterface');
  113. $executor->expects($this->never())->method('executor');
  114. $pipeline = new PipelineContext(new Client(), array('executor' => $executor));
  115. $pipeline->echo('one');
  116. $pipeline->echo('two');
  117. $pipeline->echo('three');
  118. $pipeline->flushPipeline(false);
  119. $this->assertSame(array(), $pipeline->execute());
  120. }
  121. /**
  122. * @group disconnected
  123. */
  124. public function testFlushHandlesPartialBuffers()
  125. {
  126. $connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
  127. $connection->expects($this->exactly(4))
  128. ->method('writeCommand');
  129. $connection->expects($this->exactly(4))
  130. ->method('readResponse')
  131. ->will($this->returnCallback($this->getReadCallback()));
  132. $pipeline = new PipelineContext(new Client($connection));
  133. $pipeline->echo('one');
  134. $pipeline->echo('two');
  135. $pipeline->flushPipeline();
  136. $pipeline->echo('three');
  137. $pipeline->echo('four');
  138. $this->assertSame(array('one', 'two', 'three', 'four'), $pipeline->execute());
  139. }
  140. /**
  141. * @group disconnected
  142. */
  143. public function testExecuteAcceptsCallableArgument()
  144. {
  145. $test = $this;
  146. $pipeline = new PipelineContext(new Client());
  147. $callable = function($pipe) use($test, $pipeline) {
  148. $test->assertSame($pipeline, $pipe);
  149. $pipe->flushPipeline(false);
  150. };
  151. $pipeline->execute($callable);
  152. }
  153. /**
  154. * @group disconnected
  155. * @expectedException InvalidArgumentException
  156. */
  157. public function testExecuteDoesNotAcceptNonCallableArgument()
  158. {
  159. $noncallable = new \stdClass();
  160. $pipeline = new PipelineContext(new Client());
  161. $pipeline->execute($noncallable);
  162. }
  163. /**
  164. * @group disconnected
  165. * @expectedException Predis\ClientException
  166. */
  167. public function testExecuteInsideCallableArgumentThrowsException()
  168. {
  169. $pipeline = new PipelineContext(new Client());
  170. $pipeline->execute(function($pipe) {
  171. $pipe->execute();
  172. });
  173. }
  174. /**
  175. * @group disconnected
  176. */
  177. public function testExecuteWithCallableArgumentRunsPipelineInCallable()
  178. {
  179. $connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
  180. $connection->expects($this->exactly(4))
  181. ->method('writeCommand');
  182. $connection->expects($this->exactly(4))
  183. ->method('readResponse')
  184. ->will($this->returnCallback($this->getReadCallback()));
  185. $pipeline = new PipelineContext(new Client($connection));
  186. $replies = $pipeline->execute(function($pipe) {
  187. $pipe->echo('one');
  188. $pipe->echo('two');
  189. $pipe->echo('three');
  190. $pipe->echo('four');
  191. });
  192. $this->assertSame(array('one', 'two', 'three', 'four'), $replies);
  193. }
  194. /**
  195. * @group disconnected
  196. */
  197. public function testExecuteWithCallableArgumentHandlesExceptions()
  198. {
  199. $connection = $this->getMock('Predis\Connection\SingleConnectionInterface');
  200. $connection->expects($this->never())->method('writeCommand');
  201. $connection->expects($this->never())->method('readResponse');
  202. $pipeline = new PipelineContext(new Client($connection));
  203. $exception = null;
  204. $replies = null;
  205. try {
  206. $replies = $pipeline->execute(function($pipe) {
  207. $pipe->echo('one');
  208. throw new ClientException('TEST');
  209. $pipe->echo('two');
  210. });
  211. }
  212. catch (\Exception $ex) {
  213. $exception = $ex;
  214. }
  215. $this->assertInstanceOf('Predis\ClientException', $exception);
  216. $this->assertSame('TEST', $exception->getMessage());
  217. $this->assertNull($replies);
  218. }
  219. // ******************************************************************** //
  220. // ---- INTEGRATION TESTS --------------------------------------------- //
  221. // ******************************************************************** //
  222. /**
  223. * @group connected
  224. */
  225. public function testIntegrationWithFluentInterface()
  226. {
  227. $pipeline = $this->getClient()->pipeline();
  228. $results = $pipeline->echo('one')
  229. ->echo('two')
  230. ->echo('three')
  231. ->execute();
  232. $this->assertSame(array('one', 'two', 'three'), $results);
  233. }
  234. /**
  235. * @group connected
  236. */
  237. public function testIntegrationWithCallableBlock()
  238. {
  239. $client = $this->getClient();
  240. $results = $client->pipeline(function($pipe) {
  241. $pipe->set('foo', 'bar');
  242. $pipe->get('foo');
  243. });
  244. $this->assertSame(array(true, 'bar'), $results);
  245. $this->assertTrue($client->exists('foo'));
  246. }
  247. /**
  248. * @group connected
  249. */
  250. public function testOutOfBandMessagesInsidePipeline()
  251. {
  252. $oob = null;
  253. $client = $this->getClient();
  254. $results = $client->pipeline(function($pipe) use(&$oob) {
  255. $pipe->set('foo', 'bar');
  256. $oob = $pipe->getClient()->echo('oob message');
  257. $pipe->get('foo');
  258. });
  259. $this->assertSame(array(true, 'bar'), $results);
  260. $this->assertSame('oob message', $oob);
  261. $this->assertTrue($client->exists('foo'));
  262. }
  263. /**
  264. * @group connected
  265. */
  266. public function testIntegrationWithClientExceptionInCallableBlock()
  267. {
  268. $client = $this->getClient();
  269. try {
  270. $client->pipeline(function($pipe) {
  271. $pipe->set('foo', 'bar');
  272. throw new ClientException('TEST');
  273. });
  274. }
  275. catch (\Exception $ex) {
  276. $exception = $ex;
  277. }
  278. $this->assertInstanceOf('Predis\ClientException', $exception);
  279. $this->assertSame('TEST', $exception->getMessage());
  280. $this->assertFalse($client->exists('foo'));
  281. }
  282. /**
  283. * @group connected
  284. */
  285. public function testIntegrationWithServerExceptionInCallableBlock()
  286. {
  287. $client = $this->getClient();
  288. try {
  289. $client->pipeline(function($pipe) {
  290. $pipe->set('foo', 'bar');
  291. // LPUSH on a string key fails, but won't stop
  292. // the pipeline to send the commands.
  293. $pipe->lpush('foo', 'bar');
  294. $pipe->set('hoge', 'piyo');
  295. });
  296. }
  297. catch (\Exception $ex) {
  298. $exception = $ex;
  299. }
  300. $this->assertInstanceOf('Predis\ServerException', $exception);
  301. $this->assertTrue($client->exists('foo'));
  302. $this->assertTrue($client->exists('hoge'));
  303. }
  304. /**
  305. * @group connected
  306. */
  307. public function testIntegrationWithServerErrorInCallableBlock()
  308. {
  309. $client = $this->getClient(array(), array('exceptions' => false));
  310. $results = $client->pipeline(function($pipe) {
  311. $pipe->set('foo', 'bar');
  312. $pipe->lpush('foo', 'bar'); // LPUSH on a string key fails.
  313. $pipe->get('foo');
  314. });
  315. $this->assertTrue($results[0]);
  316. $this->assertInstanceOf('Predis\ResponseError', $results[1]);
  317. $this->assertSame('bar', $results[2]);
  318. }
  319. // ******************************************************************** //
  320. // ---- HELPER METHODS ------------------------------------------------ //
  321. // ******************************************************************** //
  322. /**
  323. * Returns a client instance connected to the specified Redis
  324. * server instance to perform integration tests.
  325. *
  326. * @return array Additional connection parameters.
  327. * @return array Additional client options.
  328. * @return Client New client instance.
  329. */
  330. protected function getClient(Array $parameters = array(), Array $options = array())
  331. {
  332. $parameters = array_merge(array(
  333. 'scheme' => 'tcp',
  334. 'host' => REDIS_SERVER_HOST,
  335. 'port' => REDIS_SERVER_PORT,
  336. 'database' => REDIS_SERVER_DBNUM,
  337. ), $parameters);
  338. $options = array_merge(array(
  339. 'profile' => REDIS_SERVER_VERSION,
  340. ), $options);
  341. $client = new Client($parameters, $options);
  342. $client->connect();
  343. $client->flushdb();
  344. return $client;
  345. }
  346. /**
  347. * Helper method that returns a callback used to emulate a reply
  348. * to an ECHO command.
  349. *
  350. * @return \Closure
  351. */
  352. protected function getReadCallback()
  353. {
  354. return function($command) {
  355. if (($id = $command->getId()) !== 'ECHO') {
  356. throw new \InvalidArgumentException("Expected ECHO, got {$id}");
  357. }
  358. list($echoed) = $command->getArguments();
  359. return $echoed;
  360. };
  361. }
  362. }