PredisClientFeatures.php 29 KB

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