PredisClientFeatures.php 28 KB

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