ClientFeaturesTest.php 30 KB

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