ClientFeaturesTest.php 29 KB

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