ClientFeaturesTest.php 30 KB

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