PredisClientFeatures.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. <?php
  2. require_once 'PHPUnit/Framework.php';
  3. require_once 'PredisShared.php';
  4. require_once '../lib/Predis_Compatibility.php';
  5. class PredisClientFeaturesTestSuite extends PHPUnit_Framework_TestCase {
  6. public $redis;
  7. protected function setUp() {
  8. $this->redis = RC::getConnection();
  9. $this->redis->flushDatabase();
  10. }
  11. protected function tearDown() {
  12. }
  13. protected function onNotSuccessfulTest(Exception $exception) {
  14. // drops and reconnect to a redis server on uncaught exceptions
  15. RC::resetConnection();
  16. parent::onNotSuccessfulTest($exception);
  17. }
  18. /* ConnectionParameters */
  19. function testConnectionParametersDefaultValues() {
  20. $params = new \Predis\ConnectionParameters();
  21. $this->assertEquals(\Predis\ConnectionParameters::DEFAULT_HOST, $params->host);
  22. $this->assertEquals(\Predis\ConnectionParameters::DEFAULT_PORT, $params->port);
  23. $this->assertEquals(\Predis\ConnectionParameters::DEFAULT_TIMEOUT, $params->connection_timeout);
  24. $this->assertNull($params->read_write_timeout);
  25. $this->assertNull($params->database);
  26. $this->assertNull($params->password);
  27. $this->assertNull($params->alias);
  28. }
  29. function testConnectionParametersSetupValuesArray() {
  30. $paramsArray = RC::getConnectionParametersArgumentsArray();
  31. $params = new \Predis\ConnectionParameters($paramsArray);
  32. $this->assertEquals($paramsArray['host'], $params->host);
  33. $this->assertEquals($paramsArray['port'], $params->port);
  34. $this->assertEquals($paramsArray['connection_timeout'], $params->connection_timeout);
  35. $this->assertEquals($paramsArray['read_write_timeout'], $params->read_write_timeout);
  36. $this->assertEquals($paramsArray['database'], $params->database);
  37. $this->assertEquals($paramsArray['password'], $params->password);
  38. $this->assertEquals($paramsArray['alias'], $params->alias);
  39. }
  40. function testConnectionParametersSetupValuesString() {
  41. $paramsArray = RC::getConnectionParametersArgumentsArray();
  42. $paramsString = RC::getConnectionParametersArgumentsString($paramsArray);
  43. $params = new \Predis\ConnectionParameters($paramsArray);
  44. $this->assertEquals($paramsArray['host'], $params->host);
  45. $this->assertEquals($paramsArray['port'], $params->port);
  46. $this->assertEquals($paramsArray['connection_timeout'], $params->connection_timeout);
  47. $this->assertEquals($paramsArray['read_write_timeout'], $params->read_write_timeout);
  48. $this->assertEquals($paramsArray['database'], $params->database);
  49. $this->assertEquals($paramsArray['password'], $params->password);
  50. $this->assertEquals($paramsArray['alias'], $params->alias);
  51. }
  52. /* Command and derivates */
  53. function testCommand_TestArguments() {
  54. $cmdArgs = array('key1', 'key2', 'key3');
  55. $cmd = new \Predis\Commands\GetMultiple();
  56. $cmd->setArgumentsArray($cmdArgs);
  57. $this->assertEquals($cmdArgs[0], $cmd->getArgument(0));
  58. $this->assertEquals($cmdArgs[1], $cmd->getArgument(1));
  59. $this->assertEquals($cmdArgs[2], $cmd->getArgument(2));
  60. $cmd = new \Predis\Commands\GetMultiple();
  61. $cmd->setArguments('key1', 'key2', 'key3');
  62. $this->assertEquals($cmdArgs[0], $cmd->getArgument(0));
  63. $this->assertEquals($cmdArgs[1], $cmd->getArgument(1));
  64. $this->assertEquals($cmdArgs[2], $cmd->getArgument(2));
  65. $cmd = new \Predis\Commands\Ping();
  66. $this->assertNull($cmd->getArgument(0));
  67. }
  68. function testCommand_InlineWithNoArguments() {
  69. $cmd = new \Predis\Compatibility\v1_0\Commands\Ping();
  70. $this->assertType('\Predis\InlineCommand', $cmd);
  71. $this->assertEquals('PING', $cmd->getCommandId());
  72. $this->assertFalse($cmd->closesConnection());
  73. $this->assertFalse($cmd->canBeHashed());
  74. $this->assertNull($cmd->getHash(new \Predis\Distribution\HashRing()));
  75. $this->assertEquals("PING\r\n", $cmd());
  76. }
  77. function testCommand_InlineWithArguments() {
  78. $cmd = new \Predis\Compatibility\v1_0\Commands\Get();
  79. $cmd->setArgumentsArray(array('key'));
  80. $this->assertType('\Predis\InlineCommand', $cmd);
  81. $this->assertEquals('GET', $cmd->getCommandId());
  82. $this->assertFalse($cmd->closesConnection());
  83. $this->assertTrue($cmd->canBeHashed());
  84. $this->assertNotNull($cmd->getHash(new \Predis\Distribution\HashRing()));
  85. $this->assertEquals("GET key\r\n", $cmd());
  86. }
  87. function testCommand_BulkWithArguments() {
  88. $cmd = new \Predis\Compatibility\v1_0\Commands\Set();
  89. $cmd->setArgumentsArray(array('key', 'value'));
  90. $this->assertType('\Predis\BulkCommand', $cmd);
  91. $this->assertEquals('SET', $cmd->getCommandId());
  92. $this->assertFalse($cmd->closesConnection());
  93. $this->assertTrue($cmd->canBeHashed());
  94. $this->assertNotNull($cmd->getHash(new \Predis\Distribution\HashRing()));
  95. $this->assertEquals("SET key 5\r\nvalue\r\n", $cmd());
  96. }
  97. function testCommand_MultiBulkWithArguments() {
  98. $cmd = new \Predis\Commands\SetMultiple();
  99. $cmd->setArgumentsArray(array('key1', 'value1', 'key2', 'value2'));
  100. $this->assertType('\Predis\MultiBulkCommand', $cmd);
  101. $this->assertEquals('MSET', $cmd->getCommandId());
  102. $this->assertFalse($cmd->closesConnection());
  103. $this->assertFalse($cmd->canBeHashed());
  104. $this->assertNotNull($cmd->getHash(new \Predis\Distribution\HashRing()));
  105. $this->assertEquals("*5\r\n$4\r\nMSET\r\n$4\r\nkey1\r\n$6\r\nvalue1\r\n$4\r\nkey2\r\n$6\r\nvalue2\r\n", $cmd());
  106. }
  107. function testCommand_ParseResponse() {
  108. // default parser
  109. $cmd = new \Predis\Commands\Get();
  110. $this->assertEquals('test', $cmd->parseResponse('test'));
  111. // overridden parser (boolean)
  112. $cmd = new \Predis\Commands\Exists();
  113. $this->assertTrue($cmd->parseResponse('1'));
  114. $this->assertFalse($cmd->parseResponse('0'));
  115. // overridden parser (boolean)
  116. $cmd = new \Predis\Commands\Ping();
  117. $this->assertTrue($cmd->parseResponse('PONG'));
  118. // overridden parser (complex)
  119. // TODO: emulate a respons to INFO
  120. }
  121. /* RedisServerProfile and derivates */
  122. function testRedisServerProfile_GetSpecificVersions() {
  123. $this->assertType('\Predis\RedisServer_v1_0', \Predis\RedisServerProfile::get('1.0'));
  124. $this->assertType('\Predis\RedisServer_v1_2', \Predis\RedisServerProfile::get('1.2'));
  125. $this->assertType('\Predis\RedisServer_v2_0', \Predis\RedisServerProfile::get('2.0'));
  126. $this->assertType('\Predis\RedisServer_vNext', \Predis\RedisServerProfile::get('dev'));
  127. $this->assertType('\Predis\RedisServerProfile', \Predis\RedisServerProfile::get('default'));
  128. $this->assertEquals(\Predis\RedisServerProfile::get('default'), \Predis\RedisServerProfile::getDefault());
  129. }
  130. function testRedisServerProfile_SupportedCommands() {
  131. $profile_10 = \Predis\RedisServerProfile::get('1.0');
  132. $profile_12 = \Predis\RedisServerProfile::get('1.2');
  133. $this->assertTrue($profile_10->supportsCommand('info'));
  134. $this->assertTrue($profile_12->supportsCommand('info'));
  135. $this->assertFalse($profile_10->supportsCommand('mset'));
  136. $this->assertTrue($profile_12->supportsCommand('mset'));
  137. $this->assertFalse($profile_10->supportsCommand('multi'));
  138. $this->assertFalse($profile_12->supportsCommand('multi'));
  139. }
  140. function testRedisServerProfile_CommandsCreation() {
  141. $profile = \Predis\RedisServerProfile::get('1.0');
  142. $cmdNoArgs = $profile->createCommand('info');
  143. $this->assertType('\Predis\Compatibility\v1_0\Commands\Info', $cmdNoArgs);
  144. $this->assertNull($cmdNoArgs->getArgument());
  145. $args = array('key1', 'key2');
  146. $cmdWithArgs = $profile->createCommand('mget', $args);
  147. $this->assertType('\Predis\Compatibility\v1_0\Commands\GetMultiple', $cmdWithArgs);
  148. $this->assertEquals($args[0], $cmdWithArgs->getArgument()); // TODO: why?
  149. $this->assertEquals($args[0], $cmdWithArgs->getArgument(0));
  150. $this->assertEquals($args[1], $cmdWithArgs->getArgument(1));
  151. $bogusCommand = 'not_existing_command';
  152. $expectedMessage = "'$bogusCommand' is not a registered Redis command";
  153. RC::testForClientException($this, $expectedMessage, function()
  154. use($profile, $bogusCommand) {
  155. $profile->createCommand($bogusCommand);
  156. });
  157. }
  158. function testRedisServerProfile_CommandsRegistration() {
  159. $profile = \Predis\RedisServerProfile::get('1.0');
  160. $cmdId = 'mset';
  161. $cmdClass = '\Predis\Commands\SetMultiple';
  162. $this->assertFalse($profile->supportsCommand($cmdId));
  163. $profile->registerCommand(new $cmdClass(), $cmdId);
  164. $this->assertTrue($profile->supportsCommand($cmdId));
  165. $this->assertType($cmdClass, $profile->createCommand($cmdId));
  166. }
  167. /* ResponseQueued */
  168. function testResponseQueued() {
  169. $response = new \Predis\ResponseQueued();
  170. $this->assertTrue($response->skipParse);
  171. $this->assertTrue($response->queued);
  172. $this->assertEquals(\Predis\Protocol::QUEUED, (string)$response);
  173. }
  174. /* ResponseError */
  175. function testResponseError() {
  176. $errorMessage = 'ERROR MESSAGE';
  177. $response = new \Predis\ResponseError($errorMessage);
  178. $this->assertTrue($response->skipParse);
  179. $this->assertTrue($response->error);
  180. $this->assertEquals($errorMessage, $response->message);
  181. $this->assertEquals($errorMessage, (string)$response);
  182. }
  183. /* Connection */
  184. function testConnection_StringCastReturnsIPAndPort() {
  185. $connection = new \Predis\Connection(RC::getConnectionParameters());
  186. $this->assertEquals(RC::SERVER_HOST . ':' . RC::SERVER_PORT, (string) $connection);
  187. }
  188. function testConnection_ConnectDisconnect() {
  189. $connection = new \Predis\Connection(RC::getConnectionParameters());
  190. $this->assertFalse($connection->isConnected());
  191. $connection->connect();
  192. $this->assertTrue($connection->isConnected());
  193. $connection->disconnect();
  194. $this->assertFalse($connection->isConnected());
  195. }
  196. function testConnection_WriteAndReadCommand() {
  197. $cmd = \Predis\RedisServerProfile::getDefault()->createCommand('ping');
  198. $connection = new \Predis\Connection(RC::getConnectionParameters());
  199. $connection->connect();
  200. $connection->writeCommand($cmd);
  201. $this->assertTrue($connection->readResponse($cmd));
  202. }
  203. function testConnection_WriteCommandAndCloseConnection() {
  204. $cmd = \Predis\RedisServerProfile::getDefault()->createCommand('quit');
  205. $connection = new \Predis\Connection(new \Predis\ConnectionParameters(
  206. RC::getConnectionArguments() + array('read_write_timeout' => 0.5)
  207. ));
  208. $connection->connect();
  209. $this->assertTrue($connection->isConnected());
  210. $connection->writeCommand($cmd);
  211. $connection->disconnect();
  212. $exceptionMessage = 'Error while reading line from the server';
  213. RC::testForCommunicationException($this, $exceptionMessage, function() use($connection, $cmd) {
  214. $connection->readResponse($cmd);
  215. });
  216. }
  217. function testConnection_GetSocketOpensConnection() {
  218. $connection = new \Predis\Connection(RC::getConnectionParameters());
  219. $this->assertFalse($connection->isConnected());
  220. $this->assertType('resource', $connection->getSocket());
  221. $this->assertTrue($connection->isConnected());
  222. }
  223. function testConnection_LazyConnect() {
  224. $cmd = \Predis\RedisServerProfile::getDefault()->createCommand('ping');
  225. $connection = new \Predis\Connection(RC::getConnectionParameters());
  226. $this->assertFalse($connection->isConnected());
  227. $connection->writeCommand($cmd);
  228. $this->assertTrue($connection->isConnected());
  229. $this->assertTrue($connection->readResponse($cmd));
  230. }
  231. function testConnection_RawCommand() {
  232. $connection = new \Predis\Connection(RC::getConnectionParameters());
  233. $this->assertEquals('PONG', $connection->rawCommand("PING\r\n"));
  234. }
  235. function testConnection_Alias() {
  236. $connection1 = new \Predis\Connection(RC::getConnectionParameters());
  237. $this->assertNull($connection1->getParameters()->alias);
  238. $args = array_merge(RC::getConnectionArguments(), array('alias' => 'servername'));
  239. $connection2 = new \Predis\Connection(new \Predis\ConnectionParameters($args));
  240. $this->assertEquals('servername', $connection2->getParameters()->alias);
  241. }
  242. function testConnection_ConnectionTimeout() {
  243. $timeout = 3;
  244. $args = array('host' => '1.0.0.1', 'connection_timeout' => $timeout);
  245. $connection = new \Predis\Connection(new \Predis\ConnectionParameters($args));
  246. $start = time();
  247. RC::testForCommunicationException($this, null, function() use($connection) {
  248. $connection->connect();
  249. });
  250. $this->assertEquals((float)(time() - $start), $timeout, '', 1);
  251. }
  252. function testConnection_ReadTimeout() {
  253. $timeout = 1;
  254. $args = array_merge(RC::getConnectionArguments(), array('read_write_timeout' => $timeout));
  255. $cmdFake = \Predis\RedisServerProfile::getDefault()->createCommand('ping');
  256. $connection = new \Predis\Connection(new \Predis\ConnectionParameters($args));
  257. $expectedMessage = 'Error while reading line from the server';
  258. $start = time();
  259. RC::testForCommunicationException($this, $expectedMessage, function() use($connection, $cmdFake) {
  260. $connection->readResponse($cmdFake);
  261. });
  262. $this->assertEquals((float)(time() - $start), $timeout, '', 1);
  263. }
  264. /* ResponseReader */
  265. function testResponseReader_OptionIterableMultiBulkReplies() {
  266. $connection = new \Predis\Connection(RC::getConnectionParameters());
  267. $responseReader = $connection->getResponseReader();
  268. $responseReader->setHandler(
  269. \Predis\Protocol::PREFIX_MULTI_BULK,
  270. new \Predis\ResponseMultiBulkHandler()
  271. );
  272. $this->assertType('array', $connection->rawCommand("KEYS *\r\n"));
  273. $responseReader->setHandler(
  274. \Predis\Protocol::PREFIX_MULTI_BULK,
  275. new \Predis\ResponseMultiBulkStreamHandler()
  276. );
  277. $this->assertType('\Iterator', $connection->rawCommand("KEYS *\r\n"));
  278. }
  279. function testResponseReader_OptionExceptionOnError() {
  280. $connection = new \Predis\Connection(RC::getConnectionParameters());
  281. $responseReader = $connection->getResponseReader();
  282. $connection->rawCommand("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n");
  283. $rawCmdUnexpected = "*3\r\n$5\r\nLPUSH\r\n$3\r\nkey\r\n$5\r\nvalue\r\n";
  284. $responseReader->setHandler(
  285. \Predis\Protocol::PREFIX_ERROR,
  286. new \Predis\ResponseErrorSilentHandler()
  287. );
  288. $errorReply = $connection->rawCommand($rawCmdUnexpected);
  289. $this->assertType('\Predis\ResponseError', $errorReply);
  290. $this->assertEquals(RC::EXCEPTION_WRONG_TYPE, $errorReply->message);
  291. $responseReader->setHandler(
  292. \Predis\Protocol::PREFIX_ERROR,
  293. new \Predis\ResponseErrorHandler()
  294. );
  295. RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function()
  296. use ($connection, $rawCmdUnexpected) {
  297. $connection->rawCommand($rawCmdUnexpected);
  298. });
  299. }
  300. function testResponseReader_EmptyBulkResponse() {
  301. $this->assertTrue($this->redis->set('foo', ''));
  302. $this->assertEquals('', $this->redis->get('foo'));
  303. $this->assertEquals('', $this->redis->get('foo'));
  304. }
  305. /* Client + CommandPipeline */
  306. function testCommandPipeline_Simple() {
  307. $client = RC::getConnection();
  308. $client->flushdb();
  309. $pipe = $client->pipeline();
  310. $this->assertType('\Predis\CommandPipeline', $pipe);
  311. $this->assertType('\Predis\CommandPipeline', $pipe->set('foo', 'bar'));
  312. $this->assertType('\Predis\CommandPipeline', $pipe->set('hoge', 'piyo'));
  313. $this->assertType('\Predis\CommandPipeline', $pipe->mset(array(
  314. 'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
  315. )));
  316. $this->assertType('\Predis\CommandPipeline', $pipe->mget(array(
  317. 'foo', 'hoge', 'foofoo', 'hogehoge'
  318. )));
  319. $replies = $pipe->execute();
  320. $this->assertType('array', $replies);
  321. $this->assertEquals(4, count($replies));
  322. $this->assertEquals(4, count($replies[3]));
  323. $this->assertEquals('barbar', $replies[3][2]);
  324. }
  325. function testCommandPipeline_FluentInterface() {
  326. $client = RC::getConnection();
  327. $client->flushdb();
  328. $replies = $client->pipeline()->ping()->set('foo', 'bar')->get('foo')->execute();
  329. $this->assertType('array', $replies);
  330. $this->assertEquals('bar', $replies[2]);
  331. }
  332. function testCommandPipeline_CallableAnonymousBlock() {
  333. $client = RC::getConnection();
  334. $client->flushdb();
  335. $replies = $client->pipeline(function($pipe) {
  336. $pipe->ping();
  337. $pipe->set('foo', 'bar');
  338. $pipe->get('foo');
  339. });
  340. $this->assertType('array', $replies);
  341. $this->assertEquals('bar', $replies[2]);
  342. }
  343. function testCommandPipeline_ClientExceptionInCallableBlock() {
  344. $client = RC::getConnection();
  345. $client->flushdb();
  346. RC::testForClientException($this, 'TEST', function() use($client) {
  347. $client->pipeline(function($pipe) {
  348. $pipe->ping();
  349. $pipe->set('foo', 'bar');
  350. throw new \Predis\ClientException("TEST");
  351. });
  352. });
  353. $this->assertFalse($client->exists('foo'));
  354. }
  355. function testCommandPipeline_ServerExceptionInCallableBlock() {
  356. $client = RC::getConnection();
  357. $client->flushdb();
  358. $client->getResponseReader()->setHandler('-', new \Predis\ResponseErrorSilentHandler());
  359. $replies = $client->pipeline(function($pipe) {
  360. $pipe->set('foo', 'bar');
  361. $pipe->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
  362. $pipe->set('hoge', 'piyo');
  363. });
  364. $this->assertType('array', $replies);
  365. $this->assertType('\Predis\ResponseError', $replies[1]);
  366. $this->assertTrue($client->exists('foo'));
  367. $this->assertTrue($client->exists('hoge'));
  368. }
  369. function testCommandPipeline_Flush() {
  370. $client = RC::getConnection();
  371. $client->flushdb();
  372. $pipe = $client->pipeline();
  373. $pipe->set('foo', 'bar')->set('hoge', 'piyo');
  374. $pipe->flushPipeline();
  375. $pipe->ping()->mget(array('foo', 'hoge'));
  376. $replies = $pipe->execute();
  377. $this->assertType('array', $replies);
  378. $this->assertEquals(4, count($replies));
  379. $this->assertEquals('bar', $replies[3][0]);
  380. $this->assertEquals('piyo', $replies[3][1]);
  381. }
  382. /* Client + MultiExecBlock */
  383. function testMultiExecBlock_Simple() {
  384. $client = RC::getConnection();
  385. $client->flushdb();
  386. $multi = $client->multiExec();
  387. $this->assertType('\Predis\MultiExecBlock', $multi);
  388. $this->assertType('\Predis\MultiExecBlock', $multi->set('foo', 'bar'));
  389. $this->assertType('\Predis\MultiExecBlock', $multi->set('hoge', 'piyo'));
  390. $this->assertType('\Predis\MultiExecBlock', $multi->mset(array(
  391. 'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
  392. )));
  393. $this->assertType('\Predis\MultiExecBlock', $multi->mget(array(
  394. 'foo', 'hoge', 'foofoo', 'hogehoge'
  395. )));
  396. $replies = $multi->execute();
  397. $this->assertType('array', $replies);
  398. $this->assertEquals(4, count($replies));
  399. $this->assertEquals(4, count($replies[3]));
  400. $this->assertEquals('barbar', $replies[3][2]);
  401. }
  402. function testMultiExecBlock_FluentInterface() {
  403. $client = RC::getConnection();
  404. $client->flushdb();
  405. $replies = $client->multiExec()->ping()->set('foo', 'bar')->get('foo')->execute();
  406. $this->assertType('array', $replies);
  407. $this->assertEquals('bar', $replies[2]);
  408. }
  409. function testMultiExecBlock_CallableAnonymousBlock() {
  410. $client = RC::getConnection();
  411. $client->flushdb();
  412. $replies = $client->multiExec(function($multi) {
  413. $multi->ping();
  414. $multi->set('foo', 'bar');
  415. $multi->get('foo');
  416. });
  417. $this->assertType('array', $replies);
  418. $this->assertEquals('bar', $replies[2]);
  419. }
  420. /**
  421. * @expectedException Predis\ClientException
  422. */
  423. function testMultiExecBlock_CannotMixFluentInterfaceAndAnonymousBlock() {
  424. $emptyBlock = function($tx) { };
  425. $tx = RC::getConnection()->multiExec()->get('foo')->execute($emptyBlock);
  426. }
  427. function testMultiExecBlock_EmptyCallableBlock() {
  428. $client = RC::getConnection();
  429. $client->flushdb();
  430. $replies = $client->multiExec(function($multi) { });
  431. $this->assertEquals(0, count($replies));
  432. $options = array('cas' => true);
  433. $replies = $client->multiExec($options, function($multi) { });
  434. $this->assertEquals(0, count($replies));
  435. $options = array('cas' => true);
  436. $replies = $client->multiExec($options, function($multi) {
  437. $multi->multi();
  438. });
  439. $this->assertEquals(0, count($replies));
  440. }
  441. function testMultiExecBlock_ClientExceptionInCallableBlock() {
  442. $client = RC::getConnection();
  443. $client->flushdb();
  444. RC::testForClientException($this, 'TEST', function() use($client) {
  445. $client->multiExec(function($multi) {
  446. $multi->ping();
  447. $multi->set('foo', 'bar');
  448. throw new \Predis\ClientException("TEST");
  449. });
  450. });
  451. $this->assertFalse($client->exists('foo'));
  452. }
  453. function testMultiExecBlock_ServerExceptionInCallableBlock() {
  454. $client = RC::getConnection();
  455. $client->flushdb();
  456. $client->getResponseReader()->setHandler('-', new \Predis\ResponseErrorSilentHandler());
  457. $replies = $client->multiExec(function($multi) {
  458. $multi->set('foo', 'bar');
  459. $multi->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
  460. $multi->set('hoge', 'piyo');
  461. });
  462. $this->assertType('array', $replies);
  463. $this->assertType('\Predis\ResponseError', $replies[1]);
  464. $this->assertTrue($client->exists('foo'));
  465. $this->assertTrue($client->exists('hoge'));
  466. }
  467. function testMultiExecBlock_Discard() {
  468. $client = RC::getConnection();
  469. $client->flushdb();
  470. $replies = $client->multiExec(function($multi) {
  471. $multi->set('foo', 'bar');
  472. $multi->discard();
  473. $multi->set('hoge', 'piyo');
  474. });
  475. $this->assertEquals(1, count($replies));
  476. $this->assertFalse($client->exists('foo'));
  477. $this->assertTrue($client->exists('hoge'));
  478. }
  479. function testMultiExecBlock_DiscardEmpty() {
  480. $client = RC::getConnection();
  481. $client->flushdb();
  482. $replies = $client->multiExec(function($multi) {
  483. $multi->discard();
  484. });
  485. $this->assertEquals(0, count($replies));
  486. }
  487. function testMultiExecBlock_Watch() {
  488. $client1 = RC::getConnection();
  489. $client2 = RC::getConnection(true);
  490. $client1->flushdb();
  491. RC::testForAbortedMultiExecException($this, function()
  492. use($client1, $client2) {
  493. $client1->multiExec(array('watch' => 'sentinel'), function($multi)
  494. use ($client2) {
  495. $multi->set('sentinel', 'client1');
  496. $multi->get('sentinel');
  497. $client2->set('sentinel', 'client2');
  498. });
  499. });
  500. $this->assertEquals('client2', $client1->get('sentinel'));
  501. }
  502. function testMultiExecBlock_CheckAndSet() {
  503. $client = RC::getConnection();
  504. $client->flushdb();
  505. $client->set('foo', 'bar');
  506. $options = array('watch' => 'foo', 'cas' => true);
  507. $replies = $client->multiExec($options, function($tx) {
  508. $tx->watch('foobar');
  509. $foo = $tx->get('foo');
  510. $tx->multi();
  511. $tx->set('foobar', $foo);
  512. $tx->mget('foo', 'foobar');
  513. });
  514. $this->assertType('array', $replies);
  515. $this->assertEquals(array(true, array('bar', 'bar')), $replies);
  516. $tx = $client->multiExec($options);
  517. $tx->watch('foobar');
  518. $foo = $tx->get('foo');
  519. $replies = $tx->multi()
  520. ->set('foobar', $foo)
  521. ->mget('foo', 'foobar')
  522. ->execute();
  523. $this->assertType('array', $replies);
  524. $this->assertEquals(array(true, array('bar', 'bar')), $replies);
  525. }
  526. function testMultiExecBlock_RetryOnServerAbort() {
  527. $client1 = RC::getConnection();
  528. $client2 = RC::getConnection(true);
  529. $client1->flushdb();
  530. $retry = 3;
  531. $attempts = 0;
  532. RC::testForAbortedMultiExecException($this, function()
  533. use($client1, $client2, $retry, &$attempts) {
  534. $options = array('watch' => 'sentinel', 'retry' => $retry);
  535. $client1->multiExec($options, function($tx)
  536. use ($client2, &$attempts) {
  537. $attempts++;
  538. $tx->set('sentinel', 'client1');
  539. $tx->get('sentinel');
  540. $client2->set('sentinel', 'client2');
  541. });
  542. });
  543. $this->assertEquals('client2', $client1->get('sentinel'));
  544. $this->assertEquals($retry + 1, $attempts);
  545. $retry = 3;
  546. $attempts = 0;
  547. RC::testForAbortedMultiExecException($this, function()
  548. use($client1, $client2, $retry, &$attempts) {
  549. $options = array(
  550. 'watch' => 'sentinel',
  551. 'cas' => true,
  552. 'retry' => $retry
  553. );
  554. $client1->multiExec($options, function($tx)
  555. use ($client2, &$attempts) {
  556. $attempts++;
  557. $tx->incr('attempts');
  558. $tx->multi();
  559. $tx->set('sentinel', 'client1');
  560. $tx->get('sentinel');
  561. $client2->set('sentinel', 'client2');
  562. });
  563. });
  564. $this->assertEquals('client2', $client1->get('sentinel'));
  565. $this->assertEquals($retry + 1, $attempts);
  566. $this->assertEquals($attempts, $client1->get('attempts'));
  567. }
  568. /**
  569. * @expectedException InvalidArgumentException
  570. */
  571. function testMultiExecBlock_RetryNotAvailableWithoutBlock() {
  572. $options = array('watch' => 'foo', 'retry' => 1);
  573. $tx = RC::getConnection()->multiExec($options);
  574. $tx->multi()->get('foo')->exec();
  575. }
  576. function testMultiExecBlock_CheckAndSet_Discard() {
  577. $client = RC::getConnection();
  578. $client->flushdb();
  579. $client->set('foo', 'bar');
  580. $options = array('watch' => 'foo', 'cas' => true);
  581. $replies = $client->multiExec($options, function($tx) {
  582. $tx->watch('foobar');
  583. $foo = $tx->get('foo');
  584. $tx->multi();
  585. $tx->set('foobar', $foo);
  586. $tx->discard();
  587. $tx->mget('foo', 'foobar');
  588. });
  589. $this->assertType('array', $replies);
  590. $this->assertEquals(array(array('bar', null)), $replies);
  591. $hijack = true;
  592. $client->set('foo', 'bar');
  593. $client2 = RC::getConnection(true);
  594. $options = array('watch' => 'foo', 'cas' => true, 'retry' => 1);
  595. $replies = $client->multiExec($options, function($tx)
  596. use ($client2, &$hijack) {
  597. $foo = $tx->get('foo');
  598. $tx->multi();
  599. $tx->set('foobar', $foo);
  600. $tx->discard();
  601. if ($hijack) {
  602. $hijack = false;
  603. $client2->set('foo', 'hijacked!');
  604. }
  605. $tx->mget('foo', 'foobar');
  606. });
  607. $this->assertType('array', $replies);
  608. $this->assertEquals(array(array('hijacked!', null)), $replies);
  609. }
  610. }
  611. ?>