PredisFeaturesTest.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. <?php
  2. class PredisClientFeaturesTestSuite 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. /* Predis\ConnectionParameters */
  16. function testConnectionParametersDefaultValues() {
  17. $params = new \Predis\ConnectionParameters();
  18. $this->assertEquals('127.0.0.1', $params->host);
  19. $this->assertEquals(6379, $params->port);
  20. $this->assertEquals(5, $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. /* Predis\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_ParseResponse() {
  66. // default parser
  67. $cmd = new \Predis\Commands\Get();
  68. $this->assertEquals('test', $cmd->parseResponse('test'));
  69. // overridden parser (boolean)
  70. $cmd = new \Predis\Commands\Exists();
  71. $this->assertTrue($cmd->parseResponse('1'));
  72. $this->assertFalse($cmd->parseResponse('0'));
  73. // overridden parser (boolean)
  74. $cmd = new \Predis\Commands\Ping();
  75. $this->assertTrue($cmd->parseResponse('PONG'));
  76. // overridden parser (complex)
  77. // TODO: emulate a respons to INFO
  78. }
  79. /* Predis\Profiles\ServerProfile and derivates */
  80. function testServerProfile_GetSpecificVersions() {
  81. $this->assertType('\Predis\Profiles\ServerVersion12', \Predis\Profiles\ServerProfile::get('1.2'));
  82. $this->assertType('\Predis\Profiles\ServerVersion20', \Predis\Profiles\ServerProfile::get('2.0'));
  83. $this->assertType('\Predis\Profiles\ServerVersion22', \Predis\Profiles\ServerProfile::get('2.2'));
  84. $this->assertType('\Predis\Profiles\ServerVersionNext', \Predis\Profiles\ServerProfile::get('dev'));
  85. $this->assertType('\Predis\Profiles\ServerProfile', \Predis\Profiles\ServerProfile::get('default'));
  86. $this->assertEquals(\Predis\Profiles\ServerProfile::get('default'), \Predis\Profiles\ServerProfile::getDefault());
  87. }
  88. function testServerProfile_SupportedCommands() {
  89. $profile_12 = \Predis\Profiles\ServerProfile::get('1.2');
  90. $profile_20 = \Predis\Profiles\ServerProfile::get('2.0');
  91. $this->assertTrue($profile_12->supportsCommand('info'));
  92. $this->assertTrue($profile_20->supportsCommand('info'));
  93. $this->assertFalse($profile_12->supportsCommand('multi'));
  94. $this->assertTrue($profile_20->supportsCommand('multi'));
  95. $this->assertFalse($profile_12->supportsCommand('watch'));
  96. $this->assertFalse($profile_20->supportsCommand('watch'));
  97. }
  98. function testServerProfile_CommandsCreation() {
  99. $profile = \Predis\Profiles\ServerProfile::get('2.0');
  100. $cmdNoArgs = $profile->createCommand('info');
  101. $this->assertType('\Predis\Commands\Info', $cmdNoArgs);
  102. $this->assertNull($cmdNoArgs->getArgument());
  103. $args = array('key1', 'key2');
  104. $cmdWithArgs = $profile->createCommand('mget', $args);
  105. $this->assertType('\Predis\Commands\GetMultiple', $cmdWithArgs);
  106. $this->assertEquals($args[0], $cmdWithArgs->getArgument()); // TODO: why?
  107. $this->assertEquals($args[0], $cmdWithArgs->getArgument(0));
  108. $this->assertEquals($args[1], $cmdWithArgs->getArgument(1));
  109. $bogusCommand = 'not_existing_command';
  110. $expectedMessage = "'$bogusCommand' is not a registered Redis command";
  111. RC::testForClientException($this, $expectedMessage, function()
  112. use($profile, $bogusCommand) {
  113. $profile->createCommand($bogusCommand);
  114. });
  115. }
  116. function testServerProfile_CommandsRegistration() {
  117. $profile = \Predis\Profiles\ServerProfile::get('1.2');
  118. $cmdId = 'multi';
  119. $cmdClass = '\Predis\Commands\Multi';
  120. $this->assertFalse($profile->supportsCommand($cmdId));
  121. $profile->registerCommand(new $cmdClass(), $cmdId);
  122. $this->assertTrue($profile->supportsCommand($cmdId));
  123. $this->assertType($cmdClass, $profile->createCommand($cmdId));
  124. }
  125. /* Predis\ResponseQueued */
  126. function testResponseQueued() {
  127. $response = new \Predis\ResponseQueued();
  128. $this->assertTrue($response->queued);
  129. $this->assertEquals(\Predis\Protocols\TextProtocol::QUEUED, (string)$response);
  130. }
  131. /* Predis\ResponseError */
  132. function testResponseError() {
  133. $errorMessage = 'ERROR MESSAGE';
  134. $response = new \Predis\ResponseError($errorMessage);
  135. $this->assertTrue($response->error);
  136. $this->assertEquals($errorMessage, $response->message);
  137. $this->assertEquals($errorMessage, (string)$response);
  138. }
  139. /* Predis\Network\StreamConnection */
  140. function testStreamConnection_StringCastReturnsIPAndPort() {
  141. $connection = new \Predis\Network\StreamConnection(RC::getConnectionParameters());
  142. $this->assertEquals(RC::SERVER_HOST . ':' . RC::SERVER_PORT, (string) $connection);
  143. }
  144. function testStreamConnection_ConnectDisconnect() {
  145. $connection = new \Predis\Network\StreamConnection(RC::getConnectionParameters());
  146. $this->assertFalse($connection->isConnected());
  147. $connection->connect();
  148. $this->assertTrue($connection->isConnected());
  149. $connection->disconnect();
  150. $this->assertFalse($connection->isConnected());
  151. }
  152. function testStreamConnection_WriteAndReadCommand() {
  153. $cmd = \Predis\Profiles\ServerProfile::getDefault()->createCommand('ping');
  154. $connection = new \Predis\Network\StreamConnection(RC::getConnectionParameters());
  155. $connection->connect();
  156. $connection->writeCommand($cmd);
  157. $this->assertTrue($connection->readResponse($cmd));
  158. }
  159. function testStreamConnection_WriteCommandAndCloseConnection() {
  160. $cmd = \Predis\Profiles\ServerProfile::getDefault()->createCommand('quit');
  161. $connection = new \Predis\Network\StreamConnection(new \Predis\ConnectionParameters(
  162. RC::getConnectionArguments() + array('read_write_timeout' => 0.5)
  163. ));
  164. $connection->connect();
  165. $this->assertTrue($connection->isConnected());
  166. $connection->writeCommand($cmd);
  167. $connection->disconnect();
  168. $exceptionMessage = 'Error while reading line from the server';
  169. RC::testForCommunicationException($this, $exceptionMessage, function() use($connection, $cmd) {
  170. $connection->readResponse($cmd);
  171. });
  172. }
  173. function testStreamConnection_GetSocketOpensConnection() {
  174. $connection = new \Predis\Network\StreamConnection(RC::getConnectionParameters());
  175. $this->assertFalse($connection->isConnected());
  176. $this->assertType('resource', $connection->getResource());
  177. $this->assertTrue($connection->isConnected());
  178. }
  179. function testStreamConnection_LazyConnect() {
  180. $cmd = \Predis\Profiles\ServerProfile::getDefault()->createCommand('ping');
  181. $connection = new \Predis\Network\StreamConnection(RC::getConnectionParameters());
  182. $this->assertFalse($connection->isConnected());
  183. $connection->writeCommand($cmd);
  184. $this->assertTrue($connection->isConnected());
  185. $this->assertTrue($connection->readResponse($cmd));
  186. }
  187. function testStreamConnection_Alias() {
  188. $connection1 = new \Predis\Network\StreamConnection(RC::getConnectionParameters());
  189. $this->assertNull($connection1->getParameters()->alias);
  190. $args = array_merge(RC::getConnectionArguments(), array('alias' => 'servername'));
  191. $connection2 = new \Predis\Network\StreamConnection(new \Predis\ConnectionParameters($args));
  192. $this->assertEquals('servername', $connection2->getParameters()->alias);
  193. }
  194. function testStreamConnection_ConnectionTimeout() {
  195. $timeout = 3;
  196. $args = array('host' => '1.0.0.1', 'connection_timeout' => $timeout);
  197. $connection = new \Predis\Network\StreamConnection(new \Predis\ConnectionParameters($args));
  198. $start = time();
  199. RC::testForCommunicationException($this, null, function() use($connection) {
  200. $connection->connect();
  201. });
  202. $this->assertEquals((float)(time() - $start), $timeout, '', 1);
  203. }
  204. function testStreamConnection_ReadTimeout() {
  205. $timeout = 1;
  206. $args = array_merge(RC::getConnectionArguments(), array('read_write_timeout' => $timeout));
  207. $cmdFake = \Predis\Profiles\ServerProfile::getDefault()->createCommand('ping');
  208. $connection = new \Predis\Network\StreamConnection(new \Predis\ConnectionParameters($args));
  209. $expectedMessage = 'Error while reading line from the server';
  210. $start = time();
  211. RC::testForCommunicationException($this, $expectedMessage, function() use($connection, $cmdFake) {
  212. $connection->readResponse($cmdFake);
  213. });
  214. $this->assertEquals((float)(time() - $start), $timeout, '', 1);
  215. }
  216. /* Predis\Protocols\TextResponseReader */
  217. function testResponseReader_OptionIterableMultiBulkReplies() {
  218. $protocol = new Predis\Protocols\ComposableTextProtocol();
  219. $reader = $protocol->getReader();
  220. $connection = new \Predis\Network\ComposableStreamConnection(RC::getConnectionParameters(), $protocol);
  221. $reader->setHandler(
  222. \Predis\Protocols\TextProtocol::PREFIX_MULTI_BULK,
  223. new \Predis\Protocols\ResponseMultiBulkHandler()
  224. );
  225. $connection->writeBytes("KEYS *\r\n");
  226. $this->assertType('array', $reader->read($connection));
  227. $reader->setHandler(
  228. \Predis\Protocols\TextProtocol::PREFIX_MULTI_BULK,
  229. new \Predis\Protocols\ResponseMultiBulkStreamHandler()
  230. );
  231. $connection->writeBytes("KEYS *\r\n");
  232. $this->assertType('\Iterator', $reader->read($connection));
  233. }
  234. function testResponseReader_OptionExceptionOnError() {
  235. $protocol = new Predis\Protocols\ComposableTextProtocol();
  236. $reader = $protocol->getReader();
  237. $connection = new \Predis\Network\ComposableStreamConnection(RC::getConnectionParameters(), $protocol);
  238. $rawCmdUnexpected = "*3\r\n$5\r\nLPUSH\r\n$3\r\nkey\r\n$5\r\nvalue\r\n";
  239. $connection->writeBytes("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n");
  240. $reader->read($connection);
  241. $reader->setHandler(
  242. \Predis\Protocols\TextProtocol::PREFIX_ERROR,
  243. new \Predis\Protocols\ResponseErrorSilentHandler()
  244. );
  245. $connection->writeBytes($rawCmdUnexpected);
  246. $errorReply = $reader->read($connection);
  247. $this->assertType('\Predis\ResponseError', $errorReply);
  248. $this->assertEquals(RC::EXCEPTION_WRONG_TYPE, $errorReply->message);
  249. $reader->setHandler(
  250. \Predis\Protocols\TextProtocol::PREFIX_ERROR,
  251. new \Predis\Protocols\ResponseErrorHandler()
  252. );
  253. RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function()
  254. use ($connection, $rawCmdUnexpected) {
  255. $connection->writeBytes($rawCmdUnexpected);
  256. $connection->getProtocol()->read($connection);
  257. });
  258. }
  259. function testResponseReader_EmptyBulkResponse() {
  260. $protocol = new \Predis\Protocols\ComposableTextProtocol();
  261. $connection = new \Predis\Network\ComposableStreamConnection(RC::getConnectionParameters(), $protocol);
  262. $client = new \Predis\Client($connection);
  263. $this->assertTrue($client->set('foo', ''));
  264. $this->assertEquals('', $client->get('foo'));
  265. $this->assertEquals('', $client->get('foo'));
  266. }
  267. /* Client initialization */
  268. function testClientInitialization_SingleConnectionParameters() {
  269. $params1 = array_merge(RC::getConnectionArguments(), array(
  270. 'connection_timeout' => 10,
  271. 'read_write_timeout' => 30,
  272. 'alias' => 'connection_alias',
  273. ));
  274. $params2 = RC::getConnectionParametersArgumentsString($params1);
  275. $params3 = new \Predis\ConnectionParameters($params1);
  276. $params4 = new \Predis\Network\StreamConnection($params3);
  277. foreach (array($params1, $params2, $params3, $params4) as $params) {
  278. $client = new \Predis\Client($params);
  279. $parameters = $client->getConnection()->getParameters();
  280. $this->assertEquals($params1['host'], $parameters->host);
  281. $this->assertEquals($params1['port'], $parameters->port);
  282. $this->assertEquals($params1['connection_timeout'], $parameters->connection_timeout);
  283. $this->assertEquals($params1['read_write_timeout'], $parameters->read_write_timeout);
  284. $this->assertEquals($params1['alias'], $parameters->alias);
  285. $this->assertNull($parameters->password);
  286. }
  287. }
  288. function testClientInitialization_ClusterConnectionParameters() {
  289. $params1 = array_merge(RC::getConnectionArguments(), array(
  290. 'connection_timeout' => 10,
  291. 'read_write_timeout' => 30,
  292. ));
  293. $params2 = RC::getConnectionParametersArgumentsString($params1);
  294. $params3 = new \Predis\ConnectionParameters($params1);
  295. $params4 = new \Predis\Network\StreamConnection($params3);
  296. $client1 = new \Predis\Client(array($params1, $params2, $params3, $params4));
  297. foreach ($client1->getConnection() as $connection) {
  298. $parameters = $connection->getParameters();
  299. $this->assertEquals($params1['host'], $parameters->host);
  300. $this->assertEquals($params1['port'], $parameters->port);
  301. $this->assertEquals($params1['connection_timeout'], $parameters->connection_timeout);
  302. $this->assertEquals($params1['read_write_timeout'], $parameters->read_write_timeout);
  303. $this->assertNull($parameters->password);
  304. }
  305. $connectionCluster = $client1->getConnection();
  306. $client2 = new \Predis\Client($connectionCluster);
  307. $this->assertSame($connectionCluster, $client2->getConnection());
  308. }
  309. /* Client + CommandPipeline */
  310. function testCommandPipeline_Simple() {
  311. $client = RC::getConnection();
  312. $client->flushdb();
  313. $pipe = $client->pipeline();
  314. $this->assertType('\Predis\CommandPipeline', $pipe);
  315. $this->assertType('\Predis\CommandPipeline', $pipe->set('foo', 'bar'));
  316. $this->assertType('\Predis\CommandPipeline', $pipe->set('hoge', 'piyo'));
  317. $this->assertType('\Predis\CommandPipeline', $pipe->mset(array(
  318. 'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
  319. )));
  320. $this->assertType('\Predis\CommandPipeline', $pipe->mget(array(
  321. 'foo', 'hoge', 'foofoo', 'hogehoge'
  322. )));
  323. $replies = $pipe->execute();
  324. $this->assertType('array', $replies);
  325. $this->assertEquals(4, count($replies));
  326. $this->assertEquals(4, count($replies[3]));
  327. $this->assertEquals('barbar', $replies[3][2]);
  328. }
  329. function testCommandPipeline_FluentInterface() {
  330. $client = RC::getConnection();
  331. $client->flushdb();
  332. $replies = $client->pipeline()->ping()->set('foo', 'bar')->get('foo')->execute();
  333. $this->assertType('array', $replies);
  334. $this->assertEquals('bar', $replies[2]);
  335. }
  336. function testCommandPipeline_CallableAnonymousBlock() {
  337. $client = RC::getConnection();
  338. $client->flushdb();
  339. $replies = $client->pipeline(function($pipe) {
  340. $pipe->ping();
  341. $pipe->set('foo', 'bar');
  342. $pipe->get('foo');
  343. });
  344. $this->assertType('array', $replies);
  345. $this->assertEquals('bar', $replies[2]);
  346. }
  347. function testCommandPipeline_ClientExceptionInCallableBlock() {
  348. $client = RC::getConnection();
  349. $client->flushdb();
  350. RC::testForClientException($this, 'TEST', function() use($client) {
  351. $client->pipeline(function($pipe) {
  352. $pipe->ping();
  353. $pipe->set('foo', 'bar');
  354. throw new \Predis\ClientException("TEST");
  355. });
  356. });
  357. $this->assertFalse($client->exists('foo'));
  358. }
  359. function testCommandPipeline_ServerExceptionInCallableBlock() {
  360. $client = RC::getConnection();
  361. $client->flushdb();
  362. $client->getConnection()->setProtocolOption('throw_errors', false);
  363. $replies = $client->pipeline(function($pipe) {
  364. $pipe->set('foo', 'bar');
  365. $pipe->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
  366. $pipe->set('hoge', 'piyo');
  367. });
  368. $this->assertType('array', $replies);
  369. $this->assertType('\Predis\ResponseError', $replies[1]);
  370. $this->assertTrue($client->exists('foo'));
  371. $this->assertTrue($client->exists('hoge'));
  372. }
  373. function testCommandPipeline_Flush() {
  374. $client = RC::getConnection();
  375. $client->flushdb();
  376. $pipe = $client->pipeline();
  377. $pipe->set('foo', 'bar')->set('hoge', 'piyo');
  378. $pipe->flushPipeline();
  379. $pipe->ping()->mget(array('foo', 'hoge'));
  380. $replies = $pipe->execute();
  381. $this->assertType('array', $replies);
  382. $this->assertEquals(4, count($replies));
  383. $this->assertEquals('bar', $replies[3][0]);
  384. $this->assertEquals('piyo', $replies[3][1]);
  385. }
  386. /* Predis\Client + Predis\MultiExecContext */
  387. function testMultiExecContext_Simple() {
  388. $client = RC::getConnection();
  389. $client->flushdb();
  390. $multi = $client->multiExec();
  391. $this->assertType('\Predis\MultiExecContext', $multi);
  392. $this->assertType('\Predis\MultiExecContext', $multi->set('foo', 'bar'));
  393. $this->assertType('\Predis\MultiExecContext', $multi->set('hoge', 'piyo'));
  394. $this->assertType('\Predis\MultiExecContext', $multi->mset(array(
  395. 'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
  396. )));
  397. $this->assertType('\Predis\MultiExecContext', $multi->mget(array(
  398. 'foo', 'hoge', 'foofoo', 'hogehoge'
  399. )));
  400. $replies = $multi->execute();
  401. $this->assertType('array', $replies);
  402. $this->assertEquals(4, count($replies));
  403. $this->assertEquals(4, count($replies[3]));
  404. $this->assertEquals('barbar', $replies[3][2]);
  405. }
  406. function testMultiExecContext_FluentInterface() {
  407. $client = RC::getConnection();
  408. $client->flushdb();
  409. $replies = $client->multiExec()->ping()->set('foo', 'bar')->get('foo')->execute();
  410. $this->assertType('array', $replies);
  411. $this->assertEquals('bar', $replies[2]);
  412. }
  413. function testMultiExecContext_CallableAnonymousBlock() {
  414. $client = RC::getConnection();
  415. $client->flushdb();
  416. $replies = $client->multiExec(function($multi) {
  417. $multi->ping();
  418. $multi->set('foo', 'bar');
  419. $multi->get('foo');
  420. });
  421. $this->assertType('array', $replies);
  422. $this->assertEquals('bar', $replies[2]);
  423. }
  424. /**
  425. * @expectedException Predis\ClientException
  426. */
  427. function testMultiExecContext_CannotMixFluentInterfaceAndAnonymousBlock() {
  428. $emptyBlock = function($tx) { };
  429. $tx = RC::getConnection()->multiExec()->get('foo')->execute($emptyBlock);
  430. }
  431. function testMultiExecContext_EmptyCallableBlock() {
  432. $client = RC::getConnection();
  433. $client->flushdb();
  434. $replies = $client->multiExec(function($multi) { });
  435. $this->assertEquals(0, count($replies));
  436. $options = array('cas' => true);
  437. $replies = $client->multiExec($options, function($multi) { });
  438. $this->assertEquals(0, count($replies));
  439. $options = array('cas' => true);
  440. $replies = $client->multiExec($options, function($multi) {
  441. $multi->multi();
  442. });
  443. $this->assertEquals(0, count($replies));
  444. }
  445. function testMultiExecContext_ClientExceptionInCallableBlock() {
  446. $client = RC::getConnection();
  447. $client->flushdb();
  448. RC::testForClientException($this, 'TEST', function() use($client) {
  449. $client->multiExec(function($multi) {
  450. $multi->ping();
  451. $multi->set('foo', 'bar');
  452. throw new \Predis\ClientException("TEST");
  453. });
  454. });
  455. $this->assertFalse($client->exists('foo'));
  456. }
  457. function testMultiExecContext_ServerExceptionInCallableBlock() {
  458. $client = RC::getConnection();
  459. $client->flushdb();
  460. $client->getConnection()->setProtocolOption('throw_errors', false);
  461. $replies = $client->multiExec(function($multi) {
  462. $multi->set('foo', 'bar');
  463. $multi->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
  464. $multi->set('hoge', 'piyo');
  465. });
  466. $this->assertType('array', $replies);
  467. $this->assertType('\Predis\ResponseError', $replies[1]);
  468. $this->assertTrue($client->exists('foo'));
  469. $this->assertTrue($client->exists('hoge'));
  470. }
  471. function testMultiExecContext_Discard() {
  472. $client = RC::getConnection();
  473. $client->flushdb();
  474. $replies = $client->multiExec(function($multi) {
  475. $multi->set('foo', 'bar');
  476. $multi->discard();
  477. $multi->set('hoge', 'piyo');
  478. });
  479. $this->assertEquals(1, count($replies));
  480. $this->assertFalse($client->exists('foo'));
  481. $this->assertTrue($client->exists('hoge'));
  482. }
  483. function testMultiExecContext_DiscardEmpty() {
  484. $client = RC::getConnection();
  485. $client->flushdb();
  486. $replies = $client->multiExec(function($multi) {
  487. $multi->discard();
  488. });
  489. $this->assertEquals(0, count($replies));
  490. }
  491. function testMultiExecContext_Watch() {
  492. $client1 = RC::getConnection();
  493. $client2 = RC::getConnection(true);
  494. $client1->flushdb();
  495. RC::testForAbortedMultiExecException($this, function()
  496. use($client1, $client2) {
  497. $client1->multiExec(array('watch' => 'sentinel'), function($multi)
  498. use ($client2) {
  499. $multi->set('sentinel', 'client1');
  500. $multi->get('sentinel');
  501. $client2->set('sentinel', 'client2');
  502. });
  503. });
  504. $this->assertEquals('client2', $client1->get('sentinel'));
  505. }
  506. function testMultiExecContext_CheckAndSet() {
  507. $client = RC::getConnection();
  508. $client->flushdb();
  509. $client->set('foo', 'bar');
  510. $options = array('watch' => 'foo', 'cas' => true);
  511. $replies = $client->multiExec($options, function($tx) {
  512. $tx->watch('foobar');
  513. $foo = $tx->get('foo');
  514. $tx->multi();
  515. $tx->set('foobar', $foo);
  516. $tx->mget('foo', 'foobar');
  517. });
  518. $this->assertType('array', $replies);
  519. $this->assertEquals(array(true, array('bar', 'bar')), $replies);
  520. $tx = $client->multiExec($options);
  521. $tx->watch('foobar');
  522. $foo = $tx->get('foo');
  523. $replies = $tx->multi()
  524. ->set('foobar', $foo)
  525. ->mget('foo', 'foobar')
  526. ->execute();
  527. $this->assertType('array', $replies);
  528. $this->assertEquals(array(true, array('bar', 'bar')), $replies);
  529. }
  530. function testMultiExecContext_RetryOnServerAbort() {
  531. $client1 = RC::getConnection();
  532. $client2 = RC::getConnection(true);
  533. $client1->flushdb();
  534. $retry = 3;
  535. $attempts = 0;
  536. RC::testForAbortedMultiExecException($this, function()
  537. use($client1, $client2, $retry, &$attempts) {
  538. $options = array('watch' => 'sentinel', 'retry' => $retry);
  539. $client1->multiExec($options, function($tx)
  540. use ($client2, &$attempts) {
  541. $attempts++;
  542. $tx->set('sentinel', 'client1');
  543. $tx->get('sentinel');
  544. $client2->set('sentinel', 'client2');
  545. });
  546. });
  547. $this->assertEquals('client2', $client1->get('sentinel'));
  548. $this->assertEquals($retry + 1, $attempts);
  549. $retry = 3;
  550. $attempts = 0;
  551. RC::testForAbortedMultiExecException($this, function()
  552. use($client1, $client2, $retry, &$attempts) {
  553. $options = array(
  554. 'watch' => 'sentinel',
  555. 'cas' => true,
  556. 'retry' => $retry
  557. );
  558. $client1->multiExec($options, function($tx)
  559. use ($client2, &$attempts) {
  560. $attempts++;
  561. $tx->incr('attempts');
  562. $tx->multi();
  563. $tx->set('sentinel', 'client1');
  564. $tx->get('sentinel');
  565. $client2->set('sentinel', 'client2');
  566. });
  567. });
  568. $this->assertEquals('client2', $client1->get('sentinel'));
  569. $this->assertEquals($retry + 1, $attempts);
  570. $this->assertEquals($attempts, $client1->get('attempts'));
  571. }
  572. /**
  573. * @expectedException InvalidArgumentException
  574. */
  575. function testMultiExecContext_RetryNotAvailableWithoutBlock() {
  576. $options = array('watch' => 'foo', 'retry' => 1);
  577. $tx = RC::getConnection()->multiExec($options);
  578. $tx->multi()->get('foo')->exec();
  579. }
  580. function testMultiExecContext_CheckAndSet_Discard() {
  581. $client = RC::getConnection();
  582. $client->flushdb();
  583. $client->set('foo', 'bar');
  584. $options = array('watch' => 'foo', 'cas' => true);
  585. $replies = $client->multiExec($options, function($tx) {
  586. $tx->watch('foobar');
  587. $foo = $tx->get('foo');
  588. $tx->multi();
  589. $tx->set('foobar', $foo);
  590. $tx->discard();
  591. $tx->mget('foo', 'foobar');
  592. });
  593. $this->assertType('array', $replies);
  594. $this->assertEquals(array(array('bar', null)), $replies);
  595. $hijack = true;
  596. $client->set('foo', 'bar');
  597. $client2 = RC::getConnection(true);
  598. $options = array('watch' => 'foo', 'cas' => true, 'retry' => 1);
  599. $replies = $client->multiExec($options, function($tx)
  600. use ($client2, &$hijack) {
  601. $foo = $tx->get('foo');
  602. $tx->multi();
  603. $tx->set('foobar', $foo);
  604. $tx->discard();
  605. if ($hijack) {
  606. $hijack = false;
  607. $client2->set('foo', 'hijacked!');
  608. }
  609. $tx->mget('foo', 'foobar');
  610. });
  611. $this->assertType('array', $replies);
  612. $this->assertEquals(array(array('hijacked!', null)), $replies);
  613. }
  614. }
  615. ?>