PredisConnectionTestCase.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  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\Connection;
  11. use PredisTestCase;
  12. /**
  13. * @group realm-connection
  14. */
  15. abstract class PredisConnectionTestCase extends PredisTestCase
  16. {
  17. /**
  18. * @group disconnected
  19. */
  20. public function testConstructorDoesNotOpenConnection()
  21. {
  22. $connection = $this->createConnection();
  23. $this->assertFalse($connection->isConnected());
  24. }
  25. /**
  26. * @group disconnected
  27. */
  28. public function testSupportsSchemeTCP()
  29. {
  30. $connection = $this->createConnectionWithParams(array('scheme' => 'tcp'));
  31. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
  32. }
  33. /**
  34. * @group disconnected
  35. */
  36. public function testSupportsSchemeRedis()
  37. {
  38. $connection = $this->createConnectionWithParams(array('scheme' => 'redis'));
  39. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
  40. }
  41. /**
  42. * @group disconnected
  43. */
  44. public function testSupportsSchemeUnix()
  45. {
  46. $connection = $this->createConnectionWithParams(array('scheme' => 'unix'));
  47. $this->assertInstanceOf('Predis\Connection\NodeConnectionInterface', $connection);
  48. }
  49. /**
  50. * @group disconnected
  51. * @expectedException \InvalidArgumentException
  52. * @expectedExceptionMessage Invalid scheme: 'udp'.
  53. */
  54. public function testThrowsExceptionOnInvalidScheme()
  55. {
  56. $this->createConnectionWithParams(array('scheme' => 'udp'));
  57. }
  58. /**
  59. * @group disconnected
  60. */
  61. public function testExposesParameters()
  62. {
  63. $parameters = $this->getParameters();
  64. $connection = $this->createConnectionWithParams($parameters);
  65. $this->assertSame($parameters, $connection->getParameters());
  66. }
  67. /**
  68. * @group disconnected
  69. */
  70. public function testCanBeSerialized()
  71. {
  72. $parameters = $this->getParameters(array(
  73. 'alias' => 'redis',
  74. 'read_write_timeout' => 10,
  75. ));
  76. $connection = $this->createConnectionWithParams($parameters);
  77. $unserialized = unserialize(serialize($connection));
  78. $this->assertInstanceOf(static::CONNECTION_CLASS, $unserialized);
  79. $this->assertEquals($parameters, $unserialized->getParameters());
  80. }
  81. /**
  82. * @group disconnected
  83. * @group slow
  84. * @expectedException \Predis\Connection\ConnectionException
  85. */
  86. public function testThrowExceptionWhenUnableToConnect()
  87. {
  88. $parameters = array('host' => '169.254.10.10', 'timeout' => 0.5);
  89. $connection = $this->createConnectionWithParams($parameters, false);
  90. $connection->executeCommand($this->getCurrentProfile()->createCommand('ping'));
  91. }
  92. // ******************************************************************** //
  93. // ---- INTEGRATION TESTS --------------------------------------------- //
  94. // ******************************************************************** //
  95. /**
  96. * @group connected
  97. * @requires PHP 5.4
  98. */
  99. public function testAcceptsTcpNodelayParameter()
  100. {
  101. $connection = $this->createConnectionWithParams(array('tcp_nodelay' => false));
  102. $connection->connect();
  103. $this->assertTrue($connection->isConnected());
  104. $connection = $this->createConnectionWithParams(array('tcp_nodelay' => true));
  105. $connection->connect();
  106. $this->assertTrue($connection->isConnected());
  107. }
  108. /**
  109. * @group connected
  110. */
  111. public function testConnectForcesConnection()
  112. {
  113. $connection = $this->createConnection();
  114. $this->assertFalse($connection->isConnected());
  115. $connection->connect();
  116. $this->assertTrue($connection->isConnected());
  117. }
  118. /**
  119. * @group connected
  120. */
  121. public function testDoesNotThrowExceptionOnConnectWhenAlreadyConnected()
  122. {
  123. $connection = $this->createConnection();
  124. $connection->connect();
  125. $this->assertTrue($connection->isConnected());
  126. $connection->connect();
  127. $this->assertTrue($connection->isConnected());
  128. }
  129. /**
  130. * @group connected
  131. */
  132. public function testDisconnectForcesDisconnection()
  133. {
  134. $connection = $this->createConnection();
  135. $connection->connect();
  136. $this->assertTrue($connection->isConnected());
  137. $connection->disconnect();
  138. $this->assertFalse($connection->isConnected());
  139. }
  140. /**
  141. * @group disconnected
  142. */
  143. public function testDoesNotThrowExceptionOnDisconnectWhenAlreadyDisconnected()
  144. {
  145. $connection = $this->createConnection();
  146. $this->assertFalse($connection->isConnected());
  147. $connection->disconnect();
  148. $this->assertFalse($connection->isConnected());
  149. }
  150. /**
  151. * @group connected
  152. */
  153. public function testGetResourceForcesConnection()
  154. {
  155. $connection = $this->createConnection();
  156. $this->assertFalse($connection->isConnected());
  157. $this->assertInternalType('resource', $connection->getResource());
  158. $this->assertTrue($connection->isConnected());
  159. }
  160. /**
  161. * @group connected
  162. */
  163. public function testSendingCommandForcesConnection()
  164. {
  165. $connection = $this->createConnection();
  166. $profile = $this->getCurrentProfile();
  167. $cmdPing = $profile->createCommand('ping');
  168. $this->assertEquals('PONG', $connection->executeCommand($cmdPing));
  169. $this->assertTrue($connection->isConnected());
  170. }
  171. /**
  172. * @group connected
  173. */
  174. public function testExecutesCommandOnServer()
  175. {
  176. $profile = $this->getCurrentProfile();
  177. $cmdPing = $this->getMock($profile->getCommandClass('ping'), array('parseResponse'));
  178. $cmdPing->expects($this->never())
  179. ->method('parseResponse');
  180. $connection = $this->createConnection();
  181. $this->assertEquals('PONG', $connection->executeCommand($cmdPing));
  182. }
  183. /**
  184. * @group connected
  185. */
  186. public function testExecutesMultipleCommandsOnServer()
  187. {
  188. $profile = $this->getCurrentProfile();
  189. $cmdPing = $profile->createCommand('ping');
  190. $cmdEcho = $profile->createCommand('echo', array('echoed'));
  191. $cmdGet = $profile->createCommand('get', array('foobar'));
  192. $cmdRpush = $profile->createCommand('rpush', array('metavars', 'foo', 'hoge', 'lol'));
  193. $cmdLrange = $profile->createCommand('lrange', array('metavars', 0, -1));
  194. $connection = $this->createConnection(true);
  195. $this->assertEquals('PONG', $connection->executeCommand($cmdPing));
  196. $this->assertSame('echoed', $connection->executeCommand($cmdEcho));
  197. $this->assertNull($connection->executeCommand($cmdGet));
  198. $this->assertSame(3, $connection->executeCommand($cmdRpush));
  199. $this->assertSame(array('foo', 'hoge', 'lol'), $connection->executeCommand($cmdLrange));
  200. }
  201. /**
  202. * @group connected
  203. */
  204. public function testWritesCommandToServer()
  205. {
  206. $profile = $this->getCurrentProfile();
  207. $cmdEcho = $this->getMock($profile->getCommandClass('echo'), array('parseResponse'));
  208. $cmdEcho->setArguments(array('ECHOED'));
  209. $cmdEcho->expects($this->never())
  210. ->method('parseResponse');
  211. $connection = $this->createConnection();
  212. $connection->writeRequest($cmdEcho);
  213. $connection->disconnect();
  214. }
  215. /**
  216. * @group connected
  217. */
  218. public function testReadsCommandFromServer()
  219. {
  220. $profile = $this->getCurrentProfile();
  221. $cmdEcho = $this->getMock($profile->getCommandClass('echo'), array('parseResponse'));
  222. $cmdEcho->setArguments(array('ECHOED'));
  223. $cmdEcho->expects($this->never())
  224. ->method('parseResponse');
  225. $connection = $this->createConnection();
  226. $connection->writeRequest($cmdEcho);
  227. $this->assertSame('ECHOED', $connection->readResponse($cmdEcho));
  228. }
  229. /**
  230. * @group connected
  231. */
  232. public function testIsAbleToWriteMultipleCommandsAndReadThemBackForPipelining()
  233. {
  234. $profile = $this->getCurrentProfile();
  235. $cmdPing = $this->getMock($profile->getCommandClass('ping'), array('parseResponse'));
  236. $cmdPing->expects($this->never())
  237. ->method('parseResponse');
  238. $cmdEcho = $this->getMock($profile->getCommandClass('echo'), array('parseResponse'));
  239. $cmdEcho->setArguments(array('ECHOED'));
  240. $cmdEcho->expects($this->never())
  241. ->method('parseResponse');
  242. $connection = $this->createConnection();
  243. $connection->writeRequest($cmdPing);
  244. $connection->writeRequest($cmdEcho);
  245. $this->assertEquals('PONG', $connection->readResponse($cmdPing));
  246. $this->assertSame('ECHOED', $connection->readResponse($cmdEcho));
  247. }
  248. /**
  249. * @group connected
  250. */
  251. public function testSendsInitializationCommandsOnConnection()
  252. {
  253. $profile = $this->getCurrentProfile();
  254. $cmdPing = $this->getMock($profile->getCommandClass('ping'), array('getArguments'));
  255. $cmdPing->expects($this->once())
  256. ->method('getArguments')
  257. ->will($this->returnValue(array()));
  258. $cmdEcho = $this->getMock($profile->getCommandClass('echo'), array('getArguments'));
  259. $cmdEcho->expects($this->once())
  260. ->method('getArguments')
  261. ->will($this->returnValue(array('ECHOED')));
  262. $connection = $this->createConnection();
  263. $connection->addConnectCommand($cmdPing);
  264. $connection->addConnectCommand($cmdEcho);
  265. $connection->connect();
  266. }
  267. /**
  268. * @group connected
  269. */
  270. public function testReadsStatusResponses()
  271. {
  272. $profile = $this->getCurrentProfile();
  273. $connection = $this->createConnection(true);
  274. $connection->writeRequest($profile->createCommand('set', array('foo', 'bar')));
  275. $this->assertInstanceOf('Predis\Response\Status', $connection->read());
  276. $connection->writeRequest($profile->createCommand('ping'));
  277. $this->assertInstanceOf('Predis\Response\Status', $connection->read());
  278. $connection->writeRequest($profile->createCommand('multi'));
  279. $connection->writeRequest($profile->createCommand('ping'));
  280. $this->assertInstanceOf('Predis\Response\Status', $connection->read());
  281. $this->assertInstanceOf('Predis\Response\Status', $connection->read());
  282. }
  283. /**
  284. * @group connected
  285. */
  286. public function testReadsBulkResponses()
  287. {
  288. $profile = $this->getCurrentProfile();
  289. $connection = $this->createConnection(true);
  290. $connection->executeCommand($profile->createCommand('set', array('foo', 'bar')));
  291. $connection->writeRequest($profile->createCommand('get', array('foo')));
  292. $this->assertSame('bar', $connection->read());
  293. $connection->writeRequest($profile->createCommand('get', array('hoge')));
  294. $this->assertNull($connection->read());
  295. }
  296. /**
  297. * @group connected
  298. */
  299. public function testReadsIntegerResponses()
  300. {
  301. $profile = $this->getCurrentProfile();
  302. $connection = $this->createConnection(true);
  303. $connection->executeCommand($profile->createCommand('rpush', array('metavars', 'foo', 'hoge', 'lol')));
  304. $connection->writeRequest($profile->createCommand('llen', array('metavars')));
  305. $this->assertSame(3, $connection->read());
  306. }
  307. /**
  308. * @group connected
  309. */
  310. public function testReadsErrorResponsesAsResponseErrorObjects()
  311. {
  312. $profile = $this->getCurrentProfile();
  313. $connection = $this->createConnection(true);
  314. $connection->executeCommand($profile->createCommand('set', array('foo', 'bar')));
  315. $connection->writeRequest($profile->createCommand('rpush', array('foo', 'baz')));
  316. $this->assertInstanceOf('Predis\Response\Error', $error = $connection->read());
  317. $this->assertRegExp('/[ERR|WRONGTYPE] Operation against a key holding the wrong kind of value/', $error->getMessage());
  318. }
  319. /**
  320. * @group connected
  321. */
  322. public function testReadsMultibulkResponsesAsArrays()
  323. {
  324. $profile = $this->getCurrentProfile();
  325. $connection = $this->createConnection(true);
  326. $connection->executeCommand($profile->createCommand('rpush', array('metavars', 'foo', 'hoge', 'lol')));
  327. $connection->writeRequest($profile->createCommand('lrange', array('metavars', 0, -1)));
  328. $this->assertSame(array('foo', 'hoge', 'lol'), $connection->read());
  329. }
  330. /**
  331. * @group connected
  332. * @group slow
  333. * @expectedException \Predis\Connection\ConnectionException
  334. */
  335. public function testThrowsExceptionOnConnectionTimeout()
  336. {
  337. $connection = $this->createConnectionWithParams(array(
  338. 'host' => '169.254.10.10',
  339. 'timeout' => 0.5,
  340. ), false);
  341. $connection->connect();
  342. }
  343. /**
  344. * @group connected
  345. * @group slow
  346. * @expectedException \Predis\Connection\ConnectionException
  347. */
  348. public function testThrowsExceptionOnReadWriteTimeout()
  349. {
  350. $profile = $this->getCurrentProfile();
  351. $connection = $this->createConnectionWithParams(array(
  352. 'read_write_timeout' => 0.5,
  353. ), true);
  354. $connection->executeCommand($profile->createCommand('brpop', array('foo', 3)));
  355. }
  356. /**
  357. * @medium
  358. * @group connected
  359. * @expectedException \Predis\Protocol\ProtocolException
  360. */
  361. public function testThrowsExceptionOnProtocolDesynchronizationErrors()
  362. {
  363. $connection = $this->createConnection();
  364. $stream = $connection->getResource();
  365. $connection->writeRequest($this->getCurrentProfile()->createCommand('ping'));
  366. fread($stream, 1);
  367. $connection->read();
  368. }
  369. // ******************************************************************** //
  370. // ---- HELPER METHODS ------------------------------------------------ //
  371. // ******************************************************************** //
  372. /**
  373. * Returns a named array with the default connection parameters and their values.
  374. *
  375. * @return array Default connection parameters.
  376. */
  377. protected function getDefaultParametersArray()
  378. {
  379. return array(
  380. 'scheme' => 'tcp',
  381. 'host' => REDIS_SERVER_HOST,
  382. 'port' => REDIS_SERVER_PORT,
  383. 'database' => REDIS_SERVER_DBNUM,
  384. 'read_write_timeout' => 2,
  385. );
  386. }
  387. /**
  388. * Asserts that the connection is using a persistent resource stream.
  389. *
  390. * This assertion will trigger a connect() operation if the connection has
  391. * not been open yet.
  392. *
  393. * @param NodeConnectionInterface $connection Connection instance.
  394. */
  395. protected function assertPersistentConnection(NodeConnectionInterface $connection)
  396. {
  397. if (version_compare(PHP_VERSION, '5.4.0') < 0 || $this->isHHVM()) {
  398. $this->markTestSkipped('This test does not currently work on HHVM.');
  399. }
  400. $this->assertSame('persistent stream', get_resource_type($connection->getResource()));
  401. }
  402. /**
  403. * Asserts that the connection is not using a persistent resource stream.
  404. *
  405. * This assertion will trigger a connect() operation if the connection has
  406. * not been open yet.
  407. *
  408. * @param NodeConnectionInterface $connection Connection instance.
  409. */
  410. protected function assertNonPersistentConnection(NodeConnectionInterface $connection)
  411. {
  412. if (version_compare(PHP_VERSION, '5.4.0') < 0 || $this->isHHVM()) {
  413. $this->markTestSkipped('This test does not currently work on HHVM.');
  414. }
  415. $this->assertSame('stream', get_resource_type($connection->getResource()));
  416. }
  417. /**
  418. * Creates a new connection instance.
  419. *
  420. * @param bool $initialize Push default initialization commands (SELECT and FLUSHDB).
  421. *
  422. * @return NodeConnectionInterface
  423. */
  424. protected function createConnection($initialize = false)
  425. {
  426. return $this->createConnectionWithParams(array(), $initialize);
  427. }
  428. /**
  429. * Creates a new connection instance using additional connection parameters.
  430. *
  431. * @param mixed $parameters Additional connection parameters.
  432. * @param bool $initialize Push default initialization commands (SELECT and FLUSHDB).
  433. *
  434. * @return NodeConnectionInterface
  435. */
  436. protected function createConnectionWithParams($parameters, $initialize = false)
  437. {
  438. $class = static::CONNECTION_CLASS;
  439. $profile = $this->getCurrentProfile();
  440. if (!$parameters instanceof ParametersInterface) {
  441. $parameters = $this->getParameters($parameters);
  442. }
  443. $connection = new $class($parameters);
  444. if ($initialize) {
  445. $connection->addConnectCommand(
  446. $profile->createCommand('select', array($parameters->database))
  447. );
  448. $connection->addConnectCommand(
  449. $profile->createCommand('flushdb')
  450. );
  451. }
  452. return $connection;
  453. }
  454. }