Browse Source

[tests] Rewrite the whole test suite to allow more granular testing.

Fix also a few bugs found while rewriting the test suite.

In order to be able to run integration tests, the test suite requires
a version of Redis >= 2.4.0.

The units have been splitted into several different groups using
PHPUnit @group annotation to allow developers to enable, disable
and combine certain types of tests. The available groups are:

  - disconnected: can run without a Redis server online
  - connected: active connection to a Redis server is required
  - commands: test dedicated to a specific Redis command
  - slow: performs operations that can slow down execution;

A list of the available groups can be obtained by running

  phpunit --list-groups

Groups of tests can be disabled or enabled via the XML configuration
file or the standard command-line test runner. Please note that due
to a bug in PHPUnit, older versions ignore the --group option when
the group is excluded in the XML configuration file. Please refer to
http://github.com/sebastianbergmann/phpunit/issues/320 for details

Integration tests in the @connected group check if the command being
tested is defined in the selected server profile (see the value of
the TEST_SERVER_VERSION constant in phpunit.xml). If the command is
not defined in the target server profile, the integration test is
automatically marked as skipped.

We also provide an helper script in the bin directory that can be
used to automatically generate a file with the scheleton of a test
case for a Redis command by specifying the name of the class in the
Predis\Commands namespace. For example, to generate a test case for
SET (represented by the Predis\Commands\StringSet class):

  ./bin/generate-command-test.php --class=StringSet

The realm of a command is automatically inferred from the name of the
class, but it can be set using the --realm option.
Daniele Alessandri 13 years ago
parent
commit
371ac91777
100 changed files with 7074 additions and 3910 deletions
  1. 0 1
      .gitignore
  2. 1 2
      .travis.yml
  3. 23 10
      README.md
  4. 0 8
      TODO
  5. 237 0
      bin/generate-command-test.php
  6. 1 1
      examples/ServerSideScripting.php
  7. 1 6
      lib/Predis/Autoloader.php
  8. 2 4
      lib/Predis/Client.php
  9. 0 8
      lib/Predis/Commands/HashDelete.php
  10. 1 0
      lib/Predis/Commands/KeyKeysV12x.php
  11. 1 1
      lib/Predis/Commands/KeyRandom.php
  12. 12 0
      lib/Predis/Commands/ListPopFirstBlocking.php
  13. 6 6
      lib/Predis/Commands/PrefixHelpers.php
  14. 4 3
      lib/Predis/Commands/PrefixableCommand.php
  15. 2 1
      lib/Predis/Commands/Processors/ProcessorChain.php
  16. 10 0
      lib/Predis/Commands/PubSubUnsubscribe.php
  17. 13 7
      lib/Predis/Commands/ScriptedCommand.php
  18. 1 1
      lib/Predis/Commands/ServerEval.php
  19. 1 1
      lib/Predis/Commands/ServerInfo.php
  20. 5 1
      lib/Predis/Commands/ServerInfoV26x.php
  21. 0 8
      lib/Predis/Commands/SetAdd.php
  22. 0 8
      lib/Predis/Commands/SetRemove.php
  23. 10 8
      lib/Predis/Commands/ZSetAdd.php
  24. 0 8
      lib/Predis/Commands/ZSetRemove.php
  25. 1 1
      lib/Predis/Iterators/MultiBulkResponseSimple.php
  26. 10 2
      lib/Predis/Iterators/MultiBulkResponseTuple.php
  27. 2 6
      lib/Predis/MonitorContext.php
  28. 3 4
      lib/Predis/Network/ConnectionBase.php
  29. 5 4
      lib/Predis/Network/PhpiredisConnection.php
  30. 8 6
      lib/Predis/Network/PredisCluster.php
  31. 1 0
      lib/Predis/Network/StreamConnection.php
  32. 10 8
      lib/Predis/Network/WebdisConnection.php
  33. 22 0
      lib/Predis/NotSupportedException.php
  34. 7 5
      lib/Predis/Options/ClientCluster.php
  35. 8 0
      lib/Predis/Options/ClientConnectionFactory.php
  36. 1 1
      lib/Predis/Options/CustomOption.php
  37. 13 3
      lib/Predis/Pipeline/PipelineContext.php
  38. 17 9
      lib/Predis/Profiles/ServerProfile.php
  39. 73 73
      lib/Predis/Profiles/ServerVersion12.php
  40. 105 105
      lib/Predis/Profiles/ServerVersion20.php
  41. 120 120
      lib/Predis/Profiles/ServerVersion22.php
  42. 121 121
      lib/Predis/Profiles/ServerVersion24.php
  43. 10 10
      lib/Predis/Profiles/ServerVersionNext.php
  44. 1 2
      lib/Predis/PubSub/DispatcherLoop.php
  45. 24 14
      lib/Predis/PubSub/PubSubContext.php
  46. 9 23
      lib/Predis/Transaction/MultiExecContext.php
  47. 23 6
      phpunit.xml.dist
  48. 0 830
      test/ClientFeaturesTest.php
  49. 0 283
      test/PredisShared.php
  50. 0 2181
      test/RedisCommandsTest.php
  51. 52 0
      tests/PHPUnit/ArrayHasSameValuesConstraint.php
  52. 174 0
      tests/PHPUnit/CommandTestCase.php
  53. 387 0
      tests/PHPUnit/ConnectionTestCase.php
  54. 67 0
      tests/PHPUnit/DistributionStrategyTestCase.php
  55. 77 0
      tests/PHPUnit/ServerVersionTestCase.php
  56. 43 0
      tests/Predis/ClientExceptionTest.php
  57. 647 0
      tests/Predis/ClientTest.php
  58. 216 0
      tests/Predis/Commands/CommandTest.php
  59. 64 0
      tests/Predis/Commands/ConnectionAuthTest.php
  60. 76 0
      tests/Predis/Commands/ConnectionEchoTest.php
  61. 71 0
      tests/Predis/Commands/ConnectionPingTest.php
  62. 72 0
      tests/Predis/Commands/ConnectionQuitTest.php
  63. 86 0
      tests/Predis/Commands/ConnectionSelectTest.php
  64. 114 0
      tests/Predis/Commands/HashDeleteTest.php
  65. 103 0
      tests/Predis/Commands/HashExistsTest.php
  66. 104 0
      tests/Predis/Commands/HashGetAllTest.php
  67. 120 0
      tests/Predis/Commands/HashGetMultipleTest.php
  68. 100 0
      tests/Predis/Commands/HashGetTest.php
  69. 125 0
      tests/Predis/Commands/HashIncrementByFloatTest.php
  70. 125 0
      tests/Predis/Commands/HashIncrementByTest.php
  71. 104 0
      tests/Predis/Commands/HashKeysTest.php
  72. 99 0
      tests/Predis/Commands/HashLengthTest.php
  73. 126 0
      tests/Predis/Commands/HashSetMultipleTest.php
  74. 103 0
      tests/Predis/Commands/HashSetPreserveTest.php
  75. 102 0
      tests/Predis/Commands/HashSetTest.php
  76. 104 0
      tests/Predis/Commands/HashValuesTest.php
  77. 108 0
      tests/Predis/Commands/KeyDeleteTest.php
  78. 97 0
      tests/Predis/Commands/KeyExistsTest.php
  79. 119 0
      tests/Predis/Commands/KeyExpireAtTest.php
  80. 132 0
      tests/Predis/Commands/KeyExpireTest.php
  81. 94 0
      tests/Predis/Commands/KeyKeysTest.php
  82. 80 0
      tests/Predis/Commands/KeyKeysV12xTest.php
  83. 110 0
      tests/Predis/Commands/KeyMoveTest.php
  84. 112 0
      tests/Predis/Commands/KeyPersistTest.php
  85. 109 0
      tests/Predis/Commands/KeyPreciseExpireAtTest.php
  86. 134 0
      tests/Predis/Commands/KeyPreciseExpireTest.php
  87. 109 0
      tests/Predis/Commands/KeyPreciseTimeToLiveTest.php
  88. 87 0
      tests/Predis/Commands/KeyRandomTest.php
  89. 100 0
      tests/Predis/Commands/KeyRenamePreserveTest.php
  90. 99 0
      tests/Predis/Commands/KeyRenameTest.php
  91. 270 0
      tests/Predis/Commands/KeySortTest.php
  92. 109 0
      tests/Predis/Commands/KeyTimeToLiveTest.php
  93. 98 0
      tests/Predis/Commands/KeyTypeTest.php
  94. 115 0
      tests/Predis/Commands/ListIndexTest.php
  95. 123 0
      tests/Predis/Commands/ListInsertTest.php
  96. 110 0
      tests/Predis/Commands/ListLengthTest.php
  97. 94 0
      tests/Predis/Commands/ListPopFirstBlockingTest.php
  98. 110 0
      tests/Predis/Commands/ListPopFirstTest.php
  99. 94 0
      tests/Predis/Commands/ListPopLastBlockingTest.php
  100. 75 0
      tests/Predis/Commands/ListPopLastPushHeadBlockingTest.php

+ 0 - 1
.gitignore

@@ -1,3 +1,2 @@
 phpunit.xml
 experiments/
-test/enable.tests

+ 1 - 2
.travis.yml

@@ -2,8 +2,7 @@ language: php
 php:
   - 5.3
   - 5.4
-before_script: touch test/enable.tests
 branches:
   only:
     - master
-    - version-0.6
+    - v0.7

+ 23 - 10
README.md

@@ -148,27 +148,40 @@ $redis->newcmd();
 ```
 
 
-## Development ##
+## Test suite ##
 
-Predis is fully backed up by a test suite which tries to cover all the aspects of the
-client library and the interaction of every single command with a Redis server. If you
-want to work on Predis, it is highly recommended that you first run the test suite to
-be sure that everything is OK, and report strange behaviours or bugs.
+__ATTENTION__: Do not run the test suite shipped with Predis against instances of
+Redis running in production environments or containing data you are interested in!
 
-When modifying Predis please be sure that no warnings or notices are emitted by PHP
+Predis has a comprehensive test suite covering every aspect of the library. The suite
+performs integration tests against a running instance of Redis (>= 2.4.0 is required)
+to verify the correct behaviour of the implementation of each command and automatically
+skips commands that are not defined in the selected version of Redis. If you do not have
+Redis up and running, integration tests can be disabled. By default, the test suite is
+configured to execute integration tests using the server profile for Redis v2.4 (which
+is the current stable version of Redis). You can optionally run the suite against a
+Redis instance built from the `unstable` branch with the development profile by changing
+the `TEST_SERVER_VERSION` to `dev` in the `phpunit.xml` file. More details about testing
+Predis are available in `tests/README.md`.
+
+## Contributing ##
+
+If you want to work on Predis, it is highly recommended that you first run the test
+suite in order to check that everything is OK, and report strange behaviours or bugs.
+
+When modifying Predis please make sure that no warnings or notices are emitted by PHP
 by running the interpreter in your development environment with the `error_reporting`
 variable set to `E_ALL | E_STRICT`.
 
 The recommended way to contribute to Predis is to fork the project on GitHub, create
 new topic branches on your newly created repository to fix or add features and then
-open a new pull request with a description of the applied changes. Obviously, you can
-use any other Git hosting provider of your preference. Diff patches will be accepted
-too, even though they are not the preferred way to contribute to Predis.
+open a new pull request with a description of the applied changes. Obviously, you
+can use any other Git hosting provider of your preference.
 
 
 ## Dependencies ##
 
-- PHP >= 5.3.0
+- PHP >= 5.3.2
 - PHPUnit >= 3.5.0 (needed to run the test suite)
 
 ## Links ##

+ 0 - 8
TODO

@@ -2,13 +2,5 @@
 
 * Implement smart and transparent support for redis-cluster.
 
-* Switch to smaller test units and add more granular tests that try to cover
-  every single class.
-
 * Documentation! The README is obviously not enought to show how to use Predis
   as it does not cover all of its features.
-
-* Missing tests for commands:
-    PUBLISH, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, DEBUG, OBJECT,
-    CLIENT, CONFIG GET, CONFIG SET, CONFIG RESETSTAT, SLOWLOG, SCRIPT, PTTL,
-    PSETEX, PEXPIRE, PEXPIREAT, INCRBYFLOAT, HINCRBYFLOAT

+ 237 - 0
bin/generate-command-test.php

@@ -0,0 +1,237 @@
+#!/usr/bin/env php
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Predis\Commands\ICommand;
+use Predis\Commands\IPrefixable;
+
+class CommandTestCaseGenerator
+{
+    private $options;
+
+    public function __construct(Array $options)
+    {
+        if (!isset($options['class'])) {
+            throw new RuntimeException("Missing 'class' option.");
+        }
+        $this->options = $options;
+    }
+
+    public static function fromCommandLine()
+    {
+        $parameters = array(
+            'c:'  => 'class:',
+            'r::' => 'realm::',
+            'o::' => 'output::',
+            'x::' => 'overwrite::'
+        );
+
+        $getops = getopt(implode(array_keys($parameters)), $parameters);
+
+        $options = array(
+            'overwrite' => false,
+            'tests' => __DIR__.'/../tests',
+        );
+
+        foreach ($getops as $option => $value) {
+            switch ($option) {
+                case 'c':
+                case 'class':
+                    $options['class'] = $value;
+                    break;
+
+                case 'r':
+                case 'realm':
+                    $options['realm'] = $value;
+                    break;
+
+                case 'o':
+                case 'output':
+                    $options['output'] = $value;
+                    break;
+
+                case 'x':
+                case 'overwrite':
+                    $options['overwrite'] = true;
+                    break;
+            }
+        }
+
+        if (!isset($options['class'])) {
+            throw new RuntimeException("Missing 'class' option.");
+        }
+
+        $options['fqn'] = "Predis\\Commands\\{$options['class']}";
+        $options['path'] = "Predis/Commands/{$options['class']}.php";
+
+        $source = __DIR__.'/../lib/'.$options['path'];
+        if (!file_exists($source)) {
+            throw new RuntimeException("Cannot find class file for {$options['fqn']} in $source.");
+        }
+
+        if (!isset($options['output'])) {
+            $options['output'] = sprintf("%s/%s", $options['tests'], str_replace('.php', 'Test.php', $options['path']));
+        }
+
+        return new self($options);
+    }
+
+    protected function getTestRealm()
+    {
+        if (isset($this->options['realm'])) {
+            if (!$this->options['realm']) {
+                throw new RuntimeException('Invalid value for realm has been sepcified (empty).');
+            }
+            return $this->options['realm'];
+        }
+
+        $class = array_pop(explode('\\', $this->options['fqn']));
+        list($realm,) = preg_split('/([[:upper:]][[:lower:]]+)/', $class, 2, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+
+        return strtolower($realm);
+    }
+
+    public function generate()
+    {
+        $reflection = new ReflectionClass($class = $this->options['fqn']);
+
+        if (!$reflection->isInstantiable()) {
+            throw new RuntimeException("Class $class must be instantiable, abstract classes or interfaces are not allowed.");
+        }
+        if (!$reflection->implementsInterface('Predis\Commands\ICommand')) {
+            throw new RuntimeException("Class $class must implement the Predis\Commands\ICommand interface.");
+        }
+
+        $instance = $reflection->newInstance();
+        $buffer = $this->getTestCaseBuffer($instance);
+
+        return $buffer;
+    }
+
+    public function save()
+    {
+        $options = $this->options;
+        if (file_exists($options['output']) && !$options['overwrite']) {
+            throw new RuntimeException("File {$options['output']} already exist. Specify the --overwrite option to overwrite the existing file.");
+        }
+        file_put_contents($options['output'], $this->generate());
+    }
+
+    protected function getTestCaseBuffer(ICommand $instance)
+    {
+        $id = $instance->getId();
+        $fqn = get_class($instance);
+        $class = array_pop(explode('\\', $fqn)) . "Test";
+        $realm = $this->getTestRealm();
+
+        $buffer =<<<PHP
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-$realm
+ */
+class $class extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return '$fqn';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return '$id';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        \$this->markTestIncomplete('This test has not been implemented yet.');
+
+        \$arguments = array(/* add arguments */);
+        \$expected = array(/* add arguments */);
+
+        \$command = \$this->getCommand();
+        \$command->setArguments(\$arguments);
+
+        \$this->assertSame(\$expected, \$command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        \$this->markTestIncomplete('This test has not been implemented yet.');
+
+        \$raw = null;
+        \$expected = null;
+
+        \$command = \$this->getCommand();
+
+        \$this->assertSame(\$expected, \$command->parseResponse(\$raw));
+    }
+
+PHP;
+
+        if ($instance instanceof IPrefixable) {
+            $buffer .=<<<PHP
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        \$this->markTestIncomplete('This test has not been implemented yet.');
+
+        \$arguments = array(/* add arguments */);
+        \$expected = array(/* add arguments */);
+
+        \$command = \$this->getCommandWithArgumentsArray(\$arguments);
+        \$command->prefixKeys('prefix:');
+
+        \$this->assertSame(\$expected, \$command->getArguments());
+    }
+
+PHP;
+        }
+
+        return "$buffer}\n";
+    }
+}
+
+// ------------------------------------------------------------------------- //
+
+require __DIR__.'/../autoload.php';
+
+$generator = CommandTestCaseGenerator::fromCommandLine();
+$generator->save();

+ 1 - 1
examples/ServerSideScripting.php

@@ -20,7 +20,7 @@ use Predis\Commands\ScriptedCommand;
 
 class IncrementExistingKey extends ScriptedCommand
 {
-    protected function keysCount()
+    public function getKeysCount()
     {
         return 1;
     }

+ 1 - 6
lib/Predis/Autoloader.php

@@ -52,11 +52,6 @@ class Autoloader
         $relativeClassName = substr($className, strlen($this->prefix));
         $classNameParts = explode('\\', $relativeClassName);
 
-        $path = $this->baseDir .
-            DIRECTORY_SEPARATOR .
-            implode(DIRECTORY_SEPARATOR, $classNameParts) .
-            '.php';
-
-        require_once $path;
+        require_once $this->baseDir.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $classNameParts).'.php';
     }
 }

+ 2 - 4
lib/Predis/Client.php

@@ -191,11 +191,9 @@ class Client
     {
         if (isset($id)) {
             if (!Helpers::isCluster($this->connection)) {
-                throw new ClientException(
-                    'Retrieving connections by alias is supported only with clustered connections'
-                );
+                $message = 'Retrieving connections by alias is supported only with clustered connections';
+                throw new NotSupportedException($message);
             }
-
             return $this->connection->getConnectionById($id);
         }
 

+ 0 - 8
lib/Predis/Commands/HashDelete.php

@@ -34,12 +34,4 @@ class HashDelete extends PrefixableCommand
     {
         return Helpers::filterVariadicValues($arguments);
     }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function parseResponse($data)
-    {
-        return (bool) $data;
-    }
 }

+ 1 - 0
lib/Predis/Commands/KeyKeysV12x.php

@@ -14,6 +14,7 @@ namespace Predis\Commands;
 /**
  * @link http://redis.io/commands/keys
  * @author Daniele Alessandri <suppakilla@gmail.com>
+ * @deprecated
  */
 class KeyKeysV12x extends KeyKeys
 {

+ 1 - 1
lib/Predis/Commands/KeyRandom.php

@@ -15,7 +15,7 @@ namespace Predis\Commands;
  * @link http://redis.io/commands/randomkey
  * @author Daniele Alessandri <suppakilla@gmail.com>
  */
-class KeyRandom extends PrefixableCommand
+class KeyRandom extends Command
 {
     /**
      * {@inheritdoc}

+ 12 - 0
lib/Predis/Commands/ListPopFirstBlocking.php

@@ -25,6 +25,18 @@ class ListPopFirstBlocking extends Command implements IPrefixable
         return 'BLPOP';
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(Array $arguments)
+    {
+        if (count($arguments) === 2 && is_array($arguments[0])) {
+            list($arguments, $timeout) = $arguments;
+            array_push($arguments, $timeout);
+        }
+        return $arguments;
+    }
+
     /**
      * {@inheritdoc}
      */

+ 6 - 6
lib/Predis/Commands/PrefixHelpers.php

@@ -24,12 +24,12 @@ class PrefixHelpers
      * @param ICommand $command Command instance.
      * @param string $prefix Prefix string.
      */
-    public function first(ICommand $command, $prefix) {
-        $arguments = $command->getArguments();
-
-        $arguments[0] = "$prefix{$arguments[0]}";
-
-        $command->setRawArguments($arguments);
+    public function first(ICommand $command, $prefix)
+    {
+        if ($arguments = $command->getArguments()) {
+            $arguments[0] = "$prefix{$arguments[0]}";
+            $command->setRawArguments($arguments);
+        }
     }
 
     /**

+ 4 - 3
lib/Predis/Commands/PrefixableCommand.php

@@ -23,8 +23,9 @@ abstract class PrefixableCommand extends Command implements IPrefixable
      */
     public function prefixKeys($prefix)
     {
-        $arguments = $this->getArguments();
-        $arguments[0] = "$prefix{$arguments[0]}";
-        $this->setRawArguments($arguments);
+        if ($arguments = $this->getArguments()) {
+            $arguments[0] = "$prefix{$arguments[0]}";
+            $this->setRawArguments($arguments);
+        }
     }
 }

+ 2 - 1
lib/Predis/Commands/Processors/ProcessorChain.php

@@ -20,7 +20,7 @@ use Predis\Commands\ICommand;
  */
 class ProcessorChain implements ICommandProcessorChain, \ArrayAccess
 {
-    private $processors;
+    private $processors = array();
 
     /**
      * @param array $processors List of instances of ICommandProcessor.
@@ -127,5 +127,6 @@ class ProcessorChain implements ICommandProcessorChain, \ArrayAccess
     public function offsetUnset($index)
     {
         unset($this->processors[$index]);
+        $this->processors = array_values($this->processors);
     }
 }

+ 10 - 0
lib/Predis/Commands/PubSubUnsubscribe.php

@@ -11,6 +11,8 @@
 
 namespace Predis\Commands;
 
+use Predis\Helpers;
+
 /**
  * @link http://redis.io/commands/unsubscribe
  * @author Daniele Alessandri <suppakilla@gmail.com>
@@ -25,6 +27,14 @@ class PubSubUnsubscribe extends Command implements IPrefixable
         return 'UNSUBSCRIBE';
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(Array $arguments)
+    {
+        return Helpers::filterArrayArguments($arguments);
+    }
+
     /**
      * {@inheritdoc}
      */

+ 13 - 7
lib/Predis/Commands/ScriptedCommand.php

@@ -27,12 +27,16 @@ abstract class ScriptedCommand extends ServerEval
      */
     public abstract function getScript();
 
-    /*
+    /**
      * Gets the number of arguments that should be considered as keys.
      *
+     * @todo Should we make a scripted command act by default as a variadic
+     *       command where the first argument is the key (KEYS[1]) and the
+     *       rest is the list of values (ARGV)?
+     *
      * @return int
      */
-    protected function keysCount()
+    public function getKeysCount()
     {
         // The default behaviour for the base class is to use all the arguments
         // passed to a scripted command to populate the KEYS table in Lua.
@@ -40,18 +44,20 @@ abstract class ScriptedCommand extends ServerEval
     }
 
     /**
-     * {@inheritdoc}
+     * Returns the elements from the arguments that are identified as keys.
+     *
+     * @return array
      */
-    protected function filterArguments(Array $arguments)
+    public function getKeys()
     {
-        return array_merge(array($this->getScript(), $this->keysCount()), $arguments);
+        return array_slice($this->getArguments(), 2, $this->getKeysCount());
     }
 
     /**
      * {@inheritdoc}
      */
-    protected function getKeys()
+    protected function filterArguments(Array $arguments)
     {
-        return array_slice($this->getArguments(), 2, $this->keysCount());
+        return array_merge(array($this->getScript(), $this->getKeysCount()), $arguments);
     }
 }

+ 1 - 1
lib/Predis/Commands/ServerEval.php

@@ -36,7 +36,7 @@ class ServerEval extends Command implements IPrefixable
             $arguments[$i] = "$prefix{$arguments[$i]}";
         }
 
-        return $arguments;
+        $this->setRawArguments($arguments);
     }
 
     /**

+ 1 - 1
lib/Predis/Commands/ServerInfo.php

@@ -39,7 +39,7 @@ class ServerInfo extends Command
     public function parseResponse($data)
     {
         $info      = array();
-        $infoLines = explode("\r\n", $data, -1);
+        $infoLines = preg_split('/\r?\n/', $data);
 
         foreach ($infoLines as $row) {
             @list($k, $v) = explode(':', $row);

+ 5 - 1
lib/Predis/Commands/ServerInfoV26x.php

@@ -24,7 +24,11 @@ class ServerInfoV26x extends ServerInfo
     {
         $info = array();
         $current = null;
-        $infoLines = explode("\r\n", $data, -1);
+        $infoLines = preg_split('/\r?\n/', $data);
+
+        if (isset($infoLines[0]) && $infoLines[0][0] !== '#') {
+            return parent::parseResponse($data);
+        }
 
         foreach ($infoLines as $row) {
             if ($row === '') {

+ 0 - 8
lib/Predis/Commands/SetAdd.php

@@ -34,12 +34,4 @@ class SetAdd extends PrefixableCommand
     {
         return Helpers::filterVariadicValues($arguments);
     }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function parseResponse($data)
-    {
-        return (bool) $data;
-    }
 }

+ 0 - 8
lib/Predis/Commands/SetRemove.php

@@ -34,12 +34,4 @@ class SetRemove extends PrefixableCommand
     {
         return Helpers::filterVariadicValues($arguments);
     }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function parseResponse($data)
-    {
-        return (bool) $data;
-    }
 }

+ 10 - 8
lib/Predis/Commands/ZSetAdd.php

@@ -32,14 +32,16 @@ class ZSetAdd extends PrefixableCommand
      */
     protected function filterArguments(Array $arguments)
     {
-        return Helpers::filterVariadicValues($arguments);
-    }
+        if (count($arguments) === 2 && is_array($arguments[1])) {
+            $flattened = array($arguments[0]);
+            foreach($arguments[1] as $member => $score) {
+                $flattened[] = $score;
+                $flattened[] = $member;
+            }
 
-    /**
-     * {@inheritdoc}
-     */
-    public function parseResponse($data)
-    {
-        return (bool) $data;
+            return $flattened;
+        }
+
+        return $arguments;
     }
 }

+ 0 - 8
lib/Predis/Commands/ZSetRemove.php

@@ -34,12 +34,4 @@ class ZSetRemove extends PrefixableCommand
     {
         return Helpers::filterVariadicValues($arguments);
     }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function parseResponse($data)
-    {
-        return (bool) $data;
-    }
 }

+ 1 - 1
lib/Predis/Iterators/MultiBulkResponseSimple.php

@@ -42,7 +42,7 @@ class MultiBulkResponseSimple extends MultiBulkResponse
      */
     public function __destruct()
     {
-        $this->sync();
+        $this->sync(true);
     }
 
     /**

+ 10 - 2
lib/Predis/Iterators/MultiBulkResponseTuple.php

@@ -17,7 +17,7 @@ namespace Predis\Iterators;
  *
  * @author Daniele Alessandri <suppakilla@gmail.com>
  */
-class MultiBulkResponseTuple extends MultiBulkResponse
+class MultiBulkResponseTuple extends MultiBulkResponse implements \OuterIterator
 {
     private $iterator;
 
@@ -33,12 +33,20 @@ class MultiBulkResponseTuple extends MultiBulkResponse
         $this->replySize = $virtualSize;
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function getInnerIterator()
+    {
+        return $this->iterator;
+    }
+
     /**
      * {@inheritdoc}
      */
     public function __destruct()
     {
-        $this->iterator->sync();
+        $this->iterator->sync(true);
     }
 
     /**

+ 2 - 6
lib/Predis/MonitorContext.php

@@ -49,15 +49,11 @@ class MonitorContext implements \Iterator
     private function checkCapabilities(Client $client)
     {
         if (Helpers::isCluster($client->getConnection())) {
-            throw new ClientException(
-                'Cannot initialize a monitor context over a cluster of connections'
-            );
+            throw new NotSupportedException('Cannot initialize a monitor context over a cluster of connections');
         }
 
         if ($client->getProfile()->supportsCommand('monitor') === false) {
-            throw new ClientException(
-                'The current profile does not support the MONITOR command'
-            );
+            throw new NotSupportedException('The current profile does not support the MONITOR command');
         }
     }
 

+ 3 - 4
lib/Predis/Network/ConnectionBase.php

@@ -11,7 +11,6 @@
 
 namespace Predis\Network;
 
-use \InvalidArgumentException;
 use Predis\Helpers;
 use Predis\IReplyObject;
 use Predis\IConnectionParameters;
@@ -61,14 +60,14 @@ abstract class ConnectionBase implements IConnectionSingle
         switch ($parameters->scheme) {
             case 'unix':
                 if (!isset($parameters->path)) {
-                    throw new InvalidArgumentException('Missing UNIX domain socket path');
+                    throw new \InvalidArgumentException('Missing UNIX domain socket path');
                 }
 
             case 'tcp':
                 return $parameters;
 
             default:
-                throw new InvalidArgumentException("Invalid scheme: {$parameters->scheme}");
+                throw new \InvalidArgumentException("Invalid scheme: {$parameters->scheme}");
         }
     }
 
@@ -182,7 +181,7 @@ abstract class ConnectionBase implements IConnectionSingle
             $message .= " [$parameters]";
         }
 
-        throw new InvalidArgumentException($message);
+        throw new \InvalidArgumentException($message);
     }
 
     /**

+ 5 - 4
lib/Predis/Network/PhpiredisConnection.php

@@ -11,12 +11,13 @@
 
 namespace Predis\Network;
 
+use Predis\Commands\ICommand;
+use Predis\IConnectionParameters;
 use Predis\ResponseError;
 use Predis\ResponseQueued;
 use Predis\ClientException;
 use Predis\ServerException;
-use Predis\IConnectionParameters;
-use Predis\Commands\ICommand;
+use Predis\NotSupportedException;
 
 /**
  * This class provides the implementation of a Predis connection that uses the
@@ -47,7 +48,7 @@ class PhpiredisConnection extends ConnectionBase
     public function __construct(IConnectionParameters $parameters)
     {
         if (!function_exists('socket_create')) {
-            throw new ClientException(
+            throw new NotSupportedException(
                 'The socket extension must be loaded in order to be able to ' .
                 'use this connection class'
             );
@@ -90,7 +91,7 @@ class PhpiredisConnection extends ConnectionBase
     private function initializeReader($throw_errors = true)
     {
         if (!function_exists('phpiredis_reader_create')) {
-            throw new ClientException(
+            throw new NotSupportedException(
                 'The phpiredis extension must be loaded in order to be able to ' .
                 'use this connection class'
             );

+ 8 - 6
lib/Predis/Network/PredisCluster.php

@@ -11,10 +11,11 @@
 
 namespace Predis\Network;
 
-use Predis\Helpers;
-use Predis\ClientException;
 use Predis\Commands\ICommand;
 use Predis\Distribution\IDistributionStrategy;
+use Predis\Helpers;
+use Predis\ClientException;
+use Predis\NotSupportedException;
 use Predis\Distribution\HashRing;
 
 /**
@@ -22,6 +23,7 @@ use Predis\Distribution\HashRing;
  * implementing client-side sharding based on pluggable distribution strategies.
  *
  * @author Daniele Alessandri <suppakilla@gmail.com>
+ * @todo Add the ability to remove connections from pool.
  */
 class PredisCluster implements IConnectionCluster, \IteratorAggregate
 {
@@ -85,7 +87,8 @@ class PredisCluster implements IConnectionCluster, \IteratorAggregate
             $this->pool[] = $connection;
         }
 
-        $this->distributor->add($connection, $parameters->weight);
+        $weight = isset($parameters->weight) ? $parameters->weight : null;
+        $this->distributor->add($connection, $weight);
     }
 
     /**
@@ -99,9 +102,8 @@ class PredisCluster implements IConnectionCluster, \IteratorAggregate
             return $this->distributor->get($cmdHash);
         }
 
-        throw new ClientException(
-            sprintf("Cannot send '%s' commands to a cluster of connections", $command->getId())
-        );
+        $message = sprintf("Cannot send '%s' commands to a cluster of connections", $command->getId());
+        throw new NotSupportedException($message);
     }
 
     /**

+ 1 - 0
lib/Predis/Network/StreamConnection.php

@@ -14,6 +14,7 @@ namespace Predis\Network;
 use Predis\ResponseError;
 use Predis\ResponseQueued;
 use Predis\ServerException;
+use Predis\NotSupportedException;
 use Predis\IConnectionParameters;
 use Predis\Commands\ICommand;
 use Predis\Iterators\MultiBulkResponseSimple;

+ 10 - 8
lib/Predis/Network/WebdisConnection.php

@@ -11,12 +11,13 @@
 
 namespace Predis\Network;
 
+use Predis\Commands\ICommand;
 use Predis\IConnectionParameters;
 use Predis\ResponseError;
-use Predis\ClientException;
 use Predis\ServerException;
-use Predis\Commands\ICommand;
+use Predis\NotSupportedException;
 use Predis\Protocol\ProtocolException;
+use Predis\Network\ConnectionException;
 
 const ERR_MSG_EXTENSION = 'The %s extension must be loaded in order to be able to use this connection class';
 
@@ -74,7 +75,7 @@ class WebdisConnection implements IConnectionSingle
     private function throwNotSupportedException($function)
     {
         $class = __CLASS__;
-        throw new \RuntimeException("The method $class::$function() is not supported");
+        throw new NotSupportedException("The method $class::$function() is not supported");
     }
 
     /**
@@ -83,10 +84,10 @@ class WebdisConnection implements IConnectionSingle
     private function checkExtensions()
     {
         if (!function_exists('curl_init')) {
-            throw new ClientException(sprintf(ERR_MSG_EXTENSION, 'curl'));
+            throw new NotSupportedException(sprintf(ERR_MSG_EXTENSION, 'curl'));
         }
         if (!function_exists('phpiredis_reader_create')) {
-            throw new ClientException(sprintf(ERR_MSG_EXTENSION, 'phpiredis'));
+            throw new NotSupportedException(sprintf(ERR_MSG_EXTENSION, 'phpiredis'));
         }
     }
 
@@ -138,7 +139,7 @@ class WebdisConnection implements IConnectionSingle
      *
      * @return \Closure
      */
-    private function getStatusHandler()
+    protected function getStatusHandler()
     {
         return function($payload) {
             return $payload === 'OK' ? true : $payload;
@@ -151,7 +152,7 @@ class WebdisConnection implements IConnectionSingle
      * @param Boolean $throwErrors Specify if Redis errors throw exceptions.
      * @return \Closure
      */
-    private function getErrorHandler($throwErrors)
+    protected function getErrorHandler($throwErrors)
     {
         if ($throwErrors) {
             return function($errorMessage) {
@@ -218,7 +219,8 @@ class WebdisConnection implements IConnectionSingle
             case 'WATCH':
             case 'UNWATCH':
             case 'DISCARD':
-                throw new \InvalidArgumentException("Disabled command: {$command->getId()}");
+            case 'MONITOR':
+                throw new NotSupportedException("Disabled command: {$command->getId()}");
 
             default:
                 return $commandId;

+ 22 - 0
lib/Predis/NotSupportedException.php

@@ -0,0 +1,22 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis;
+
+/**
+ * Exception class generated when trying to use features not
+ * supported by certain classes or abstractions.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class NotSupportedException extends PredisException
+{
+}

+ 7 - 5
lib/Predis/Options/ClientCluster.php

@@ -30,9 +30,7 @@ class ClientCluster extends Option
     protected function checkInstance($cluster)
     {
         if (!$cluster instanceof IConnectionCluster) {
-            throw new \InvalidArgumentException(
-                'Instance of Predis\Network\IConnectionCluster expected'
-            );
+            throw new \InvalidArgumentException('Instance of Predis\Network\IConnectionCluster expected');
         }
 
         return $cluster;
@@ -44,7 +42,7 @@ class ClientCluster extends Option
     public function validate(IClientOptions $options, $value)
     {
         if (is_callable($value)) {
-            return $this->checkInstance(call_user_func($value));
+            return $this->checkInstance(call_user_func($value, $options));
         }
         $initializer = $this->getInitializer($options, $value);
 
@@ -54,7 +52,7 @@ class ClientCluster extends Option
     /**
      * Returns an initializer for the specified FQN or type.
      *
-     * @param string $fqnOrType Type of cluster of FQN of a class implementing IConnectionCluster.
+     * @param string $fqnOrType Type of cluster or FQN of a class implementing IConnectionCluster.
      * @param IClientOptions $options Instance of the client options.
      * @return \Closure
      */
@@ -65,6 +63,10 @@ class ClientCluster extends Option
                 return function() { return new PredisCluster(); };
 
             default:
+                // TODO: we should not even allow non-string values here.
+                if (is_string($fqnOrType) && !class_exists($fqnOrType)) {
+                    throw new \InvalidArgumentException('Class $fqnOrType does not exist');
+                }
                 return function() use($fqnOrType) {
                     return new $fqnOrType();
                 };

+ 8 - 0
lib/Predis/Options/ClientConnectionFactory.php

@@ -36,6 +36,14 @@ class ClientConnectionFactory extends Option
             }
             return $factory;
         }
+        if (is_string($value) && class_exists($value)) {
+            if (!($factory = new $value()) && !$factory instanceof IConnectionFactory) {
+                throw new \InvalidArgumentException("Class $value must be an instance of Predis\IConnectionFactory");
+            }
+            return $factory;
+        }
+
+        throw new \InvalidArgumentException('Invalid value for the connections option');
     }
 
     /**

+ 1 - 1
lib/Predis/Options/CustomOption.php

@@ -24,7 +24,7 @@ class CustomOption implements IOption
     /**
      * @param array $options List of options
      */
-    public function __construct(Array $options)
+    public function __construct(Array $options = array())
     {
         $this->validate = $this->filterCallable($options, 'validate');
         $this->default  = $this->filterCallable($options, 'default');

+ 13 - 3
lib/Predis/Pipeline/PipelineContext.php

@@ -38,7 +38,7 @@ class PipelineContext
     public function __construct(Client $client, Array $options = null)
     {
         $this->client = $client;
-        $this->executor = $this->getExecutor($client, $options ?: array());
+        $this->executor = $this->createExecutor($client, $options ?: array());
     }
 
     /**
@@ -49,7 +49,7 @@ class PipelineContext
      * @param array Options for the context initialization.
      * @return IPipelineExecutor
      */
-    protected function getExecutor(Client $client, Array $options)
+    protected function createExecutor(Client $client, Array $options)
     {
         if (!$options) {
             return new StandardExecutor();
@@ -160,7 +160,7 @@ class PipelineContext
 
         try {
             if ($callable !== null) {
-                $callable($this);
+                call_user_func($callable, $this);
             }
             $this->flushPipeline();
         }
@@ -186,4 +186,14 @@ class PipelineContext
     {
         return $this->client;
     }
+
+    /**
+     * Returns the underlying pipeline executor used by the pipeline object.
+     *
+     * @return IPipelineExecutor
+     */
+    public function getExecutor()
+    {
+        return $this->executor;
+    }
 }

+ 17 - 9
lib/Predis/Profiles/ServerProfile.php

@@ -96,9 +96,7 @@ abstract class ServerProfile implements IServerProfile, IProcessingSupport
         $profileReflection = new \ReflectionClass($profileClass);
 
         if (!$profileReflection->isSubclassOf('Predis\Profiles\IServerProfile')) {
-            throw new ClientException(
-                "Cannot register '$profileClass' as it is not a valid profile class"
-            );
+            throw new \InvalidArgumentException("Cannot register '$profileClass' as it is not a valid profile class");
         }
 
         self::$profiles[$alias] = $profileClass;
@@ -146,6 +144,20 @@ abstract class ServerProfile implements IServerProfile, IProcessingSupport
         return isset($this->commands[strtolower($command)]);
     }
 
+    /**
+     * Returns the FQN of the class that represent the specified command ID
+     * registered in the current server profile.
+     *
+     * @param string $command Command ID.
+     * @return string
+     */
+    public function getCommandClass($command)
+    {
+        if (isset($this->commands[$command = strtolower($command)])) {
+            return $this->commands[$command];
+        }
+    }
+
     /**
      * {@inheritdoc}
      */
@@ -189,7 +201,7 @@ abstract class ServerProfile implements IServerProfile, IProcessingSupport
     {
         $commandReflection = new \ReflectionClass($command);
         if (!$commandReflection->isSubclassOf('Predis\Commands\ICommand')) {
-            throw new ClientException("Cannot register '$command' as it is not a valid Redis command");
+            throw new \InvalidArgumentException("Cannot register '$command' as it is not a valid Redis command");
         }
         $this->commands[strtolower($alias)] = $command;
     }
@@ -197,12 +209,8 @@ abstract class ServerProfile implements IServerProfile, IProcessingSupport
     /**
      * {@inheritdoc}
      */
-    public function setProcessor(ICommandProcessor $processor)
+    public function setProcessor(ICommandProcessor $processor = null)
     {
-        if (!isset($processor)) {
-            unset($this->processor);
-            return;
-        }
         $this->processor = $processor;
     }
 

+ 73 - 73
lib/Predis/Profiles/ServerVersion12.php

@@ -35,91 +35,91 @@ class ServerVersion12 extends ServerProfile
             /* ---------------- Redis 1.2 ---------------- */
 
             /* commands operating on the key space */
-            'exists'                    => '\Predis\Commands\KeyExists',
-            'del'                       => '\Predis\Commands\KeyDelete',
-            'type'                      => '\Predis\Commands\KeyType',
-            'keys'                      => '\Predis\Commands\KeyKeysV12x',
-            'randomkey'                 => '\Predis\Commands\KeyRandom',
-            'rename'                    => '\Predis\Commands\KeyRename',
-            'renamenx'                  => '\Predis\Commands\KeyRenamePreserve',
-            'expire'                    => '\Predis\Commands\KeyExpire',
-            'expireat'                  => '\Predis\Commands\KeyExpireAt',
-            'ttl'                       => '\Predis\Commands\KeyTimeToLive',
-            'move'                      => '\Predis\Commands\KeyMove',
-            'sort'                      => '\Predis\Commands\KeySort',
+            'exists'                    => 'Predis\Commands\KeyExists',
+            'del'                       => 'Predis\Commands\KeyDelete',
+            'type'                      => 'Predis\Commands\KeyType',
+            'keys'                      => 'Predis\Commands\KeyKeysV12x',
+            'randomkey'                 => 'Predis\Commands\KeyRandom',
+            'rename'                    => 'Predis\Commands\KeyRename',
+            'renamenx'                  => 'Predis\Commands\KeyRenamePreserve',
+            'expire'                    => 'Predis\Commands\KeyExpire',
+            'expireat'                  => 'Predis\Commands\KeyExpireAt',
+            'ttl'                       => 'Predis\Commands\KeyTimeToLive',
+            'move'                      => 'Predis\Commands\KeyMove',
+            'sort'                      => 'Predis\Commands\KeySort',
 
             /* commands operating on string values */
-            'set'                       => '\Predis\Commands\StringSet',
-            'setnx'                     => '\Predis\Commands\StringSetPreserve',
-            'mset'                      => '\Predis\Commands\StringSetMultiple',
-            'msetnx'                    => '\Predis\Commands\StringSetMultiplePreserve',
-            'get'                       => '\Predis\Commands\StringGet',
-            'mget'                      => '\Predis\Commands\StringGetMultiple',
-            'getset'                    => '\Predis\Commands\StringGetSet',
-            'incr'                      => '\Predis\Commands\StringIncrement',
-            'incrby'                    => '\Predis\Commands\StringIncrementBy',
-            'decr'                      => '\Predis\Commands\StringDecrement',
-            'decrby'                    => '\Predis\Commands\StringDecrementBy',
+            'set'                       => 'Predis\Commands\StringSet',
+            'setnx'                     => 'Predis\Commands\StringSetPreserve',
+            'mset'                      => 'Predis\Commands\StringSetMultiple',
+            'msetnx'                    => 'Predis\Commands\StringSetMultiplePreserve',
+            'get'                       => 'Predis\Commands\StringGet',
+            'mget'                      => 'Predis\Commands\StringGetMultiple',
+            'getset'                    => 'Predis\Commands\StringGetSet',
+            'incr'                      => 'Predis\Commands\StringIncrement',
+            'incrby'                    => 'Predis\Commands\StringIncrementBy',
+            'decr'                      => 'Predis\Commands\StringDecrement',
+            'decrby'                    => 'Predis\Commands\StringDecrementBy',
 
             /* commands operating on lists */
-            'rpush'                     => '\Predis\Commands\ListPushTail',
-            'lpush'                     => '\Predis\Commands\ListPushHead',
-            'llen'                      => '\Predis\Commands\ListLength',
-            'lrange'                    => '\Predis\Commands\ListRange',
-            'ltrim'                     => '\Predis\Commands\ListTrim',
-            'lindex'                    => '\Predis\Commands\ListIndex',
-            'lset'                      => '\Predis\Commands\ListSet',
-            'lrem'                      => '\Predis\Commands\ListRemove',
-            'lpop'                      => '\Predis\Commands\ListPopFirst',
-            'rpop'                      => '\Predis\Commands\ListPopLast',
-            'rpoplpush'                 => '\Predis\Commands\ListPopLastPushHead',
+            'rpush'                     => 'Predis\Commands\ListPushTail',
+            'lpush'                     => 'Predis\Commands\ListPushHead',
+            'llen'                      => 'Predis\Commands\ListLength',
+            'lrange'                    => 'Predis\Commands\ListRange',
+            'ltrim'                     => 'Predis\Commands\ListTrim',
+            'lindex'                    => 'Predis\Commands\ListIndex',
+            'lset'                      => 'Predis\Commands\ListSet',
+            'lrem'                      => 'Predis\Commands\ListRemove',
+            'lpop'                      => 'Predis\Commands\ListPopFirst',
+            'rpop'                      => 'Predis\Commands\ListPopLast',
+            'rpoplpush'                 => 'Predis\Commands\ListPopLastPushHead',
 
             /* commands operating on sets */
-            'sadd'                      => '\Predis\Commands\SetAdd',
-            'srem'                      => '\Predis\Commands\SetRemove',
-            'spop'                      => '\Predis\Commands\SetPop',
-            'smove'                     => '\Predis\Commands\SetMove',
-            'scard'                     => '\Predis\Commands\SetCardinality',
-            'sismember'                 => '\Predis\Commands\SetIsMember',
-            'sinter'                    => '\Predis\Commands\SetIntersection',
-            'sinterstore'               => '\Predis\Commands\SetIntersectionStore',
-            'sunion'                    => '\Predis\Commands\SetUnion',
-            'sunionstore'               => '\Predis\Commands\SetUnionStore',
-            'sdiff'                     => '\Predis\Commands\SetDifference',
-            'sdiffstore'                => '\Predis\Commands\SetDifferenceStore',
-            'smembers'                  => '\Predis\Commands\SetMembers',
-            'srandmember'               => '\Predis\Commands\SetRandomMember',
+            'sadd'                      => 'Predis\Commands\SetAdd',
+            'srem'                      => 'Predis\Commands\SetRemove',
+            'spop'                      => 'Predis\Commands\SetPop',
+            'smove'                     => 'Predis\Commands\SetMove',
+            'scard'                     => 'Predis\Commands\SetCardinality',
+            'sismember'                 => 'Predis\Commands\SetIsMember',
+            'sinter'                    => 'Predis\Commands\SetIntersection',
+            'sinterstore'               => 'Predis\Commands\SetIntersectionStore',
+            'sunion'                    => 'Predis\Commands\SetUnion',
+            'sunionstore'               => 'Predis\Commands\SetUnionStore',
+            'sdiff'                     => 'Predis\Commands\SetDifference',
+            'sdiffstore'                => 'Predis\Commands\SetDifferenceStore',
+            'smembers'                  => 'Predis\Commands\SetMembers',
+            'srandmember'               => 'Predis\Commands\SetRandomMember',
 
             /* commands operating on sorted sets */
-            'zadd'                      => '\Predis\Commands\ZSetAdd',
-            'zincrby'                   => '\Predis\Commands\ZSetIncrementBy',
-            'zrem'                      => '\Predis\Commands\ZSetRemove',
-            'zrange'                    => '\Predis\Commands\ZSetRange',
-            'zrevrange'                 => '\Predis\Commands\ZSetReverseRange',
-            'zrangebyscore'             => '\Predis\Commands\ZSetRangeByScore',
-            'zcard'                     => '\Predis\Commands\ZSetCardinality',
-            'zscore'                    => '\Predis\Commands\ZSetScore',
-            'zremrangebyscore'          => '\Predis\Commands\ZSetRemoveRangeByScore',
+            'zadd'                      => 'Predis\Commands\ZSetAdd',
+            'zincrby'                   => 'Predis\Commands\ZSetIncrementBy',
+            'zrem'                      => 'Predis\Commands\ZSetRemove',
+            'zrange'                    => 'Predis\Commands\ZSetRange',
+            'zrevrange'                 => 'Predis\Commands\ZSetReverseRange',
+            'zrangebyscore'             => 'Predis\Commands\ZSetRangeByScore',
+            'zcard'                     => 'Predis\Commands\ZSetCardinality',
+            'zscore'                    => 'Predis\Commands\ZSetScore',
+            'zremrangebyscore'          => 'Predis\Commands\ZSetRemoveRangeByScore',
 
             /* connection related commands */
-            'ping'                      => '\Predis\Commands\ConnectionPing',
-            'auth'                      => '\Predis\Commands\ConnectionAuth',
-            'select'                    => '\Predis\Commands\ConnectionSelect',
-            'echo'                      => '\Predis\Commands\ConnectionEcho',
-            'quit'                      => '\Predis\Commands\ConnectionQuit',
+            'ping'                      => 'Predis\Commands\ConnectionPing',
+            'auth'                      => 'Predis\Commands\ConnectionAuth',
+            'select'                    => 'Predis\Commands\ConnectionSelect',
+            'echo'                      => 'Predis\Commands\ConnectionEcho',
+            'quit'                      => 'Predis\Commands\ConnectionQuit',
 
             /* remote server control commands */
-            'info'                      => '\Predis\Commands\ServerInfo',
-            'slaveof'                   => '\Predis\Commands\ServerSlaveOf',
-            'monitor'                   => '\Predis\Commands\ServerMonitor',
-            'dbsize'                    => '\Predis\Commands\ServerDatabaseSize',
-            'flushdb'                   => '\Predis\Commands\ServerFlushDatabase',
-            'flushall'                  => '\Predis\Commands\ServerFlushAll',
-            'save'                      => '\Predis\Commands\ServerSave',
-            'bgsave'                    => '\Predis\Commands\ServerBackgroundSave',
-            'lastsave'                  => '\Predis\Commands\ServerLastSave',
-            'shutdown'                  => '\Predis\Commands\ServerShutdown',
-            'bgrewriteaof'              => '\Predis\Commands\ServerBackgroundRewriteAOF',
+            'info'                      => 'Predis\Commands\ServerInfo',
+            'slaveof'                   => 'Predis\Commands\ServerSlaveOf',
+            'monitor'                   => 'Predis\Commands\ServerMonitor',
+            'dbsize'                    => 'Predis\Commands\ServerDatabaseSize',
+            'flushdb'                   => 'Predis\Commands\ServerFlushDatabase',
+            'flushall'                  => 'Predis\Commands\ServerFlushAll',
+            'save'                      => 'Predis\Commands\ServerSave',
+            'bgsave'                    => 'Predis\Commands\ServerBackgroundSave',
+            'lastsave'                  => 'Predis\Commands\ServerLastSave',
+            'shutdown'                  => 'Predis\Commands\ServerShutdown',
+            'bgrewriteaof'              => 'Predis\Commands\ServerBackgroundRewriteAOF',
         );
     }
 }

+ 105 - 105
lib/Predis/Profiles/ServerVersion20.php

@@ -35,140 +35,140 @@ class ServerVersion20 extends ServerProfile
             /* ---------------- Redis 1.2 ---------------- */
 
             /* commands operating on the key space */
-            'exists'                    => '\Predis\Commands\KeyExists',
-            'del'                       => '\Predis\Commands\KeyDelete',
-            'type'                      => '\Predis\Commands\KeyType',
-            'keys'                      => '\Predis\Commands\KeyKeys',
-            'randomkey'                 => '\Predis\Commands\KeyRandom',
-            'rename'                    => '\Predis\Commands\KeyRename',
-            'renamenx'                  => '\Predis\Commands\KeyRenamePreserve',
-            'expire'                    => '\Predis\Commands\KeyExpire',
-            'expireat'                  => '\Predis\Commands\KeyExpireAt',
-            'ttl'                       => '\Predis\Commands\KeyTimeToLive',
-            'move'                      => '\Predis\Commands\KeyMove',
-            'sort'                      => '\Predis\Commands\KeySort',
+            'exists'                    => 'Predis\Commands\KeyExists',
+            'del'                       => 'Predis\Commands\KeyDelete',
+            'type'                      => 'Predis\Commands\KeyType',
+            'keys'                      => 'Predis\Commands\KeyKeys',
+            'randomkey'                 => 'Predis\Commands\KeyRandom',
+            'rename'                    => 'Predis\Commands\KeyRename',
+            'renamenx'                  => 'Predis\Commands\KeyRenamePreserve',
+            'expire'                    => 'Predis\Commands\KeyExpire',
+            'expireat'                  => 'Predis\Commands\KeyExpireAt',
+            'ttl'                       => 'Predis\Commands\KeyTimeToLive',
+            'move'                      => 'Predis\Commands\KeyMove',
+            'sort'                      => 'Predis\Commands\KeySort',
 
             /* commands operating on string values */
-            'set'                       => '\Predis\Commands\StringSet',
-            'setnx'                     => '\Predis\Commands\StringSetPreserve',
-            'mset'                      => '\Predis\Commands\StringSetMultiple',
-            'msetnx'                    => '\Predis\Commands\StringSetMultiplePreserve',
-            'get'                       => '\Predis\Commands\StringGet',
-            'mget'                      => '\Predis\Commands\StringGetMultiple',
-            'getset'                    => '\Predis\Commands\StringGetSet',
-            'incr'                      => '\Predis\Commands\StringIncrement',
-            'incrby'                    => '\Predis\Commands\StringIncrementBy',
-            'decr'                      => '\Predis\Commands\StringDecrement',
-            'decrby'                    => '\Predis\Commands\StringDecrementBy',
+            'set'                       => 'Predis\Commands\StringSet',
+            'setnx'                     => 'Predis\Commands\StringSetPreserve',
+            'mset'                      => 'Predis\Commands\StringSetMultiple',
+            'msetnx'                    => 'Predis\Commands\StringSetMultiplePreserve',
+            'get'                       => 'Predis\Commands\StringGet',
+            'mget'                      => 'Predis\Commands\StringGetMultiple',
+            'getset'                    => 'Predis\Commands\StringGetSet',
+            'incr'                      => 'Predis\Commands\StringIncrement',
+            'incrby'                    => 'Predis\Commands\StringIncrementBy',
+            'decr'                      => 'Predis\Commands\StringDecrement',
+            'decrby'                    => 'Predis\Commands\StringDecrementBy',
 
             /* commands operating on lists */
-            'rpush'                     => '\Predis\Commands\ListPushTail',
-            'lpush'                     => '\Predis\Commands\ListPushHead',
-            'llen'                      => '\Predis\Commands\ListLength',
-            'lrange'                    => '\Predis\Commands\ListRange',
-            'ltrim'                     => '\Predis\Commands\ListTrim',
-            'lindex'                    => '\Predis\Commands\ListIndex',
-            'lset'                      => '\Predis\Commands\ListSet',
-            'lrem'                      => '\Predis\Commands\ListRemove',
-            'lpop'                      => '\Predis\Commands\ListPopFirst',
-            'rpop'                      => '\Predis\Commands\ListPopLast',
-            'rpoplpush'                 => '\Predis\Commands\ListPopLastPushHead',
+            'rpush'                     => 'Predis\Commands\ListPushTail',
+            'lpush'                     => 'Predis\Commands\ListPushHead',
+            'llen'                      => 'Predis\Commands\ListLength',
+            'lrange'                    => 'Predis\Commands\ListRange',
+            'ltrim'                     => 'Predis\Commands\ListTrim',
+            'lindex'                    => 'Predis\Commands\ListIndex',
+            'lset'                      => 'Predis\Commands\ListSet',
+            'lrem'                      => 'Predis\Commands\ListRemove',
+            'lpop'                      => 'Predis\Commands\ListPopFirst',
+            'rpop'                      => 'Predis\Commands\ListPopLast',
+            'rpoplpush'                 => 'Predis\Commands\ListPopLastPushHead',
 
             /* commands operating on sets */
-            'sadd'                      => '\Predis\Commands\SetAdd',
-            'srem'                      => '\Predis\Commands\SetRemove',
-            'spop'                      => '\Predis\Commands\SetPop',
-            'smove'                     => '\Predis\Commands\SetMove',
-            'scard'                     => '\Predis\Commands\SetCardinality',
-            'sismember'                 => '\Predis\Commands\SetIsMember',
-            'sinter'                    => '\Predis\Commands\SetIntersection',
-            'sinterstore'               => '\Predis\Commands\SetIntersectionStore',
-            'sunion'                    => '\Predis\Commands\SetUnion',
-            'sunionstore'               => '\Predis\Commands\SetUnionStore',
-            'sdiff'                     => '\Predis\Commands\SetDifference',
-            'sdiffstore'                => '\Predis\Commands\SetDifferenceStore',
-            'smembers'                  => '\Predis\Commands\SetMembers',
-            'srandmember'               => '\Predis\Commands\SetRandomMember',
+            'sadd'                      => 'Predis\Commands\SetAdd',
+            'srem'                      => 'Predis\Commands\SetRemove',
+            'spop'                      => 'Predis\Commands\SetPop',
+            'smove'                     => 'Predis\Commands\SetMove',
+            'scard'                     => 'Predis\Commands\SetCardinality',
+            'sismember'                 => 'Predis\Commands\SetIsMember',
+            'sinter'                    => 'Predis\Commands\SetIntersection',
+            'sinterstore'               => 'Predis\Commands\SetIntersectionStore',
+            'sunion'                    => 'Predis\Commands\SetUnion',
+            'sunionstore'               => 'Predis\Commands\SetUnionStore',
+            'sdiff'                     => 'Predis\Commands\SetDifference',
+            'sdiffstore'                => 'Predis\Commands\SetDifferenceStore',
+            'smembers'                  => 'Predis\Commands\SetMembers',
+            'srandmember'               => 'Predis\Commands\SetRandomMember',
 
             /* commands operating on sorted sets */
-            'zadd'                      => '\Predis\Commands\ZSetAdd',
-            'zincrby'                   => '\Predis\Commands\ZSetIncrementBy',
-            'zrem'                      => '\Predis\Commands\ZSetRemove',
-            'zrange'                    => '\Predis\Commands\ZSetRange',
-            'zrevrange'                 => '\Predis\Commands\ZSetReverseRange',
-            'zrangebyscore'             => '\Predis\Commands\ZSetRangeByScore',
-            'zcard'                     => '\Predis\Commands\ZSetCardinality',
-            'zscore'                    => '\Predis\Commands\ZSetScore',
-            'zremrangebyscore'          => '\Predis\Commands\ZSetRemoveRangeByScore',
+            'zadd'                      => 'Predis\Commands\ZSetAdd',
+            'zincrby'                   => 'Predis\Commands\ZSetIncrementBy',
+            'zrem'                      => 'Predis\Commands\ZSetRemove',
+            'zrange'                    => 'Predis\Commands\ZSetRange',
+            'zrevrange'                 => 'Predis\Commands\ZSetReverseRange',
+            'zrangebyscore'             => 'Predis\Commands\ZSetRangeByScore',
+            'zcard'                     => 'Predis\Commands\ZSetCardinality',
+            'zscore'                    => 'Predis\Commands\ZSetScore',
+            'zremrangebyscore'          => 'Predis\Commands\ZSetRemoveRangeByScore',
 
             /* connection related commands */
-            'ping'                      => '\Predis\Commands\ConnectionPing',
-            'auth'                      => '\Predis\Commands\ConnectionAuth',
-            'select'                    => '\Predis\Commands\ConnectionSelect',
-            'echo'                      => '\Predis\Commands\ConnectionEcho',
-            'quit'                      => '\Predis\Commands\ConnectionQuit',
+            'ping'                      => 'Predis\Commands\ConnectionPing',
+            'auth'                      => 'Predis\Commands\ConnectionAuth',
+            'select'                    => 'Predis\Commands\ConnectionSelect',
+            'echo'                      => 'Predis\Commands\ConnectionEcho',
+            'quit'                      => 'Predis\Commands\ConnectionQuit',
 
             /* remote server control commands */
-            'info'                      => '\Predis\Commands\ServerInfo',
-            'slaveof'                   => '\Predis\Commands\ServerSlaveOf',
-            'monitor'                   => '\Predis\Commands\ServerMonitor',
-            'dbsize'                    => '\Predis\Commands\ServerDatabaseSize',
-            'flushdb'                   => '\Predis\Commands\ServerFlushDatabase',
-            'flushall'                  => '\Predis\Commands\ServerFlushAll',
-            'save'                      => '\Predis\Commands\ServerSave',
-            'bgsave'                    => '\Predis\Commands\ServerBackgroundSave',
-            'lastsave'                  => '\Predis\Commands\ServerLastSave',
-            'shutdown'                  => '\Predis\Commands\ServerShutdown',
-            'bgrewriteaof'              => '\Predis\Commands\ServerBackgroundRewriteAOF',
+            'info'                      => 'Predis\Commands\ServerInfo',
+            'slaveof'                   => 'Predis\Commands\ServerSlaveOf',
+            'monitor'                   => 'Predis\Commands\ServerMonitor',
+            'dbsize'                    => 'Predis\Commands\ServerDatabaseSize',
+            'flushdb'                   => 'Predis\Commands\ServerFlushDatabase',
+            'flushall'                  => 'Predis\Commands\ServerFlushAll',
+            'save'                      => 'Predis\Commands\ServerSave',
+            'bgsave'                    => 'Predis\Commands\ServerBackgroundSave',
+            'lastsave'                  => 'Predis\Commands\ServerLastSave',
+            'shutdown'                  => 'Predis\Commands\ServerShutdown',
+            'bgrewriteaof'              => 'Predis\Commands\ServerBackgroundRewriteAOF',
 
 
             /* ---------------- Redis 2.0 ---------------- */
 
             /* commands operating on string values */
-            'setex'                     => '\Predis\Commands\StringSetExpire',
-            'append'                    => '\Predis\Commands\StringAppend',
-            'substr'                    => '\Predis\Commands\StringSubstr',
+            'setex'                     => 'Predis\Commands\StringSetExpire',
+            'append'                    => 'Predis\Commands\StringAppend',
+            'substr'                    => 'Predis\Commands\StringSubstr',
 
             /* commands operating on lists */
-            'blpop'                     => '\Predis\Commands\ListPopFirstBlocking',
-            'brpop'                     => '\Predis\Commands\ListPopLastBlocking',
+            'blpop'                     => 'Predis\Commands\ListPopFirstBlocking',
+            'brpop'                     => 'Predis\Commands\ListPopLastBlocking',
 
             /* commands operating on sorted sets */
-            'zunionstore'               => '\Predis\Commands\ZSetUnionStore',
-            'zinterstore'               => '\Predis\Commands\ZSetIntersectionStore',
-            'zcount'                    => '\Predis\Commands\ZSetCount',
-            'zrank'                     => '\Predis\Commands\ZSetRank',
-            'zrevrank'                  => '\Predis\Commands\ZSetReverseRank',
-            'zremrangebyrank'           => '\Predis\Commands\ZSetRemoveRangeByRank',
+            'zunionstore'               => 'Predis\Commands\ZSetUnionStore',
+            'zinterstore'               => 'Predis\Commands\ZSetIntersectionStore',
+            'zcount'                    => 'Predis\Commands\ZSetCount',
+            'zrank'                     => 'Predis\Commands\ZSetRank',
+            'zrevrank'                  => 'Predis\Commands\ZSetReverseRank',
+            'zremrangebyrank'           => 'Predis\Commands\ZSetRemoveRangeByRank',
 
             /* commands operating on hashes */
-            'hset'                      => '\Predis\Commands\HashSet',
-            'hsetnx'                    => '\Predis\Commands\HashSetPreserve',
-            'hmset'                     => '\Predis\Commands\HashSetMultiple',
-            'hincrby'                   => '\Predis\Commands\HashIncrementBy',
-            'hget'                      => '\Predis\Commands\HashGet',
-            'hmget'                     => '\Predis\Commands\HashGetMultiple',
-            'hdel'                      => '\Predis\Commands\HashDelete',
-            'hexists'                   => '\Predis\Commands\HashExists',
-            'hlen'                      => '\Predis\Commands\HashLength',
-            'hkeys'                     => '\Predis\Commands\HashKeys',
-            'hvals'                     => '\Predis\Commands\HashValues',
-            'hgetall'                   => '\Predis\Commands\HashGetAll',
+            'hset'                      => 'Predis\Commands\HashSet',
+            'hsetnx'                    => 'Predis\Commands\HashSetPreserve',
+            'hmset'                     => 'Predis\Commands\HashSetMultiple',
+            'hincrby'                   => 'Predis\Commands\HashIncrementBy',
+            'hget'                      => 'Predis\Commands\HashGet',
+            'hmget'                     => 'Predis\Commands\HashGetMultiple',
+            'hdel'                      => 'Predis\Commands\HashDelete',
+            'hexists'                   => 'Predis\Commands\HashExists',
+            'hlen'                      => 'Predis\Commands\HashLength',
+            'hkeys'                     => 'Predis\Commands\HashKeys',
+            'hvals'                     => 'Predis\Commands\HashValues',
+            'hgetall'                   => 'Predis\Commands\HashGetAll',
 
             /* transactions */
-            'multi'                     => '\Predis\Commands\TransactionMulti',
-            'exec'                      => '\Predis\Commands\TransactionExec',
-            'discard'                   => '\Predis\Commands\TransactionDiscard',
+            'multi'                     => 'Predis\Commands\TransactionMulti',
+            'exec'                      => 'Predis\Commands\TransactionExec',
+            'discard'                   => 'Predis\Commands\TransactionDiscard',
 
             /* publish - subscribe */
-            'subscribe'                 => '\Predis\Commands\PubSubSubscribe',
-            'unsubscribe'               => '\Predis\Commands\PubSubUnsubscribe',
-            'psubscribe'                => '\Predis\Commands\PubSubSubscribeByPattern',
-            'punsubscribe'              => '\Predis\Commands\PubSubUnsubscribeByPattern',
-            'publish'                   => '\Predis\Commands\PubSubPublish',
+            'subscribe'                 => 'Predis\Commands\PubSubSubscribe',
+            'unsubscribe'               => 'Predis\Commands\PubSubUnsubscribe',
+            'psubscribe'                => 'Predis\Commands\PubSubSubscribeByPattern',
+            'punsubscribe'              => 'Predis\Commands\PubSubUnsubscribeByPattern',
+            'publish'                   => 'Predis\Commands\PubSubPublish',
 
             /* remote server control commands */
-            'config'                    => '\Predis\Commands\ServerConfig',
+            'config'                    => 'Predis\Commands\ServerConfig',
         );
     }
 }

+ 120 - 120
lib/Predis/Profiles/ServerVersion22.php

@@ -35,170 +35,170 @@ class ServerVersion22 extends ServerProfile
             /* ---------------- Redis 1.2 ---------------- */
 
             /* commands operating on the key space */
-            'exists'                    => '\Predis\Commands\KeyExists',
-            'del'                       => '\Predis\Commands\KeyDelete',
-            'type'                      => '\Predis\Commands\KeyType',
-            'keys'                      => '\Predis\Commands\KeyKeys',
-            'randomkey'                 => '\Predis\Commands\KeyRandom',
-            'rename'                    => '\Predis\Commands\KeyRename',
-            'renamenx'                  => '\Predis\Commands\KeyRenamePreserve',
-            'expire'                    => '\Predis\Commands\KeyExpire',
-            'expireat'                  => '\Predis\Commands\KeyExpireAt',
-            'ttl'                       => '\Predis\Commands\KeyTimeToLive',
-            'move'                      => '\Predis\Commands\KeyMove',
-            'sort'                      => '\Predis\Commands\KeySort',
+            'exists'                    => 'Predis\Commands\KeyExists',
+            'del'                       => 'Predis\Commands\KeyDelete',
+            'type'                      => 'Predis\Commands\KeyType',
+            'keys'                      => 'Predis\Commands\KeyKeys',
+            'randomkey'                 => 'Predis\Commands\KeyRandom',
+            'rename'                    => 'Predis\Commands\KeyRename',
+            'renamenx'                  => 'Predis\Commands\KeyRenamePreserve',
+            'expire'                    => 'Predis\Commands\KeyExpire',
+            'expireat'                  => 'Predis\Commands\KeyExpireAt',
+            'ttl'                       => 'Predis\Commands\KeyTimeToLive',
+            'move'                      => 'Predis\Commands\KeyMove',
+            'sort'                      => 'Predis\Commands\KeySort',
 
             /* commands operating on string values */
-            'set'                       => '\Predis\Commands\StringSet',
-            'setnx'                     => '\Predis\Commands\StringSetPreserve',
-            'mset'                      => '\Predis\Commands\StringSetMultiple',
-            'msetnx'                    => '\Predis\Commands\StringSetMultiplePreserve',
-            'get'                       => '\Predis\Commands\StringGet',
-            'mget'                      => '\Predis\Commands\StringGetMultiple',
-            'getset'                    => '\Predis\Commands\StringGetSet',
-            'incr'                      => '\Predis\Commands\StringIncrement',
-            'incrby'                    => '\Predis\Commands\StringIncrementBy',
-            'decr'                      => '\Predis\Commands\StringDecrement',
-            'decrby'                    => '\Predis\Commands\StringDecrementBy',
+            'set'                       => 'Predis\Commands\StringSet',
+            'setnx'                     => 'Predis\Commands\StringSetPreserve',
+            'mset'                      => 'Predis\Commands\StringSetMultiple',
+            'msetnx'                    => 'Predis\Commands\StringSetMultiplePreserve',
+            'get'                       => 'Predis\Commands\StringGet',
+            'mget'                      => 'Predis\Commands\StringGetMultiple',
+            'getset'                    => 'Predis\Commands\StringGetSet',
+            'incr'                      => 'Predis\Commands\StringIncrement',
+            'incrby'                    => 'Predis\Commands\StringIncrementBy',
+            'decr'                      => 'Predis\Commands\StringDecrement',
+            'decrby'                    => 'Predis\Commands\StringDecrementBy',
 
             /* commands operating on lists */
-            'rpush'                     => '\Predis\Commands\ListPushTail',
-            'lpush'                     => '\Predis\Commands\ListPushHead',
-            'llen'                      => '\Predis\Commands\ListLength',
-            'lrange'                    => '\Predis\Commands\ListRange',
-            'ltrim'                     => '\Predis\Commands\ListTrim',
-            'lindex'                    => '\Predis\Commands\ListIndex',
-            'lset'                      => '\Predis\Commands\ListSet',
-            'lrem'                      => '\Predis\Commands\ListRemove',
-            'lpop'                      => '\Predis\Commands\ListPopFirst',
-            'rpop'                      => '\Predis\Commands\ListPopLast',
-            'rpoplpush'                 => '\Predis\Commands\ListPopLastPushHead',
+            'rpush'                     => 'Predis\Commands\ListPushTail',
+            'lpush'                     => 'Predis\Commands\ListPushHead',
+            'llen'                      => 'Predis\Commands\ListLength',
+            'lrange'                    => 'Predis\Commands\ListRange',
+            'ltrim'                     => 'Predis\Commands\ListTrim',
+            'lindex'                    => 'Predis\Commands\ListIndex',
+            'lset'                      => 'Predis\Commands\ListSet',
+            'lrem'                      => 'Predis\Commands\ListRemove',
+            'lpop'                      => 'Predis\Commands\ListPopFirst',
+            'rpop'                      => 'Predis\Commands\ListPopLast',
+            'rpoplpush'                 => 'Predis\Commands\ListPopLastPushHead',
 
             /* commands operating on sets */
-            'sadd'                      => '\Predis\Commands\SetAdd',
-            'srem'                      => '\Predis\Commands\SetRemove',
-            'spop'                      => '\Predis\Commands\SetPop',
-            'smove'                     => '\Predis\Commands\SetMove',
-            'scard'                     => '\Predis\Commands\SetCardinality',
-            'sismember'                 => '\Predis\Commands\SetIsMember',
-            'sinter'                    => '\Predis\Commands\SetIntersection',
-            'sinterstore'               => '\Predis\Commands\SetIntersectionStore',
-            'sunion'                    => '\Predis\Commands\SetUnion',
-            'sunionstore'               => '\Predis\Commands\SetUnionStore',
-            'sdiff'                     => '\Predis\Commands\SetDifference',
-            'sdiffstore'                => '\Predis\Commands\SetDifferenceStore',
-            'smembers'                  => '\Predis\Commands\SetMembers',
-            'srandmember'               => '\Predis\Commands\SetRandomMember',
+            'sadd'                      => 'Predis\Commands\SetAdd',
+            'srem'                      => 'Predis\Commands\SetRemove',
+            'spop'                      => 'Predis\Commands\SetPop',
+            'smove'                     => 'Predis\Commands\SetMove',
+            'scard'                     => 'Predis\Commands\SetCardinality',
+            'sismember'                 => 'Predis\Commands\SetIsMember',
+            'sinter'                    => 'Predis\Commands\SetIntersection',
+            'sinterstore'               => 'Predis\Commands\SetIntersectionStore',
+            'sunion'                    => 'Predis\Commands\SetUnion',
+            'sunionstore'               => 'Predis\Commands\SetUnionStore',
+            'sdiff'                     => 'Predis\Commands\SetDifference',
+            'sdiffstore'                => 'Predis\Commands\SetDifferenceStore',
+            'smembers'                  => 'Predis\Commands\SetMembers',
+            'srandmember'               => 'Predis\Commands\SetRandomMember',
 
             /* commands operating on sorted sets */
-            'zadd'                      => '\Predis\Commands\ZSetAdd',
-            'zincrby'                   => '\Predis\Commands\ZSetIncrementBy',
-            'zrem'                      => '\Predis\Commands\ZSetRemove',
-            'zrange'                    => '\Predis\Commands\ZSetRange',
-            'zrevrange'                 => '\Predis\Commands\ZSetReverseRange',
-            'zrangebyscore'             => '\Predis\Commands\ZSetRangeByScore',
-            'zcard'                     => '\Predis\Commands\ZSetCardinality',
-            'zscore'                    => '\Predis\Commands\ZSetScore',
-            'zremrangebyscore'          => '\Predis\Commands\ZSetRemoveRangeByScore',
+            'zadd'                      => 'Predis\Commands\ZSetAdd',
+            'zincrby'                   => 'Predis\Commands\ZSetIncrementBy',
+            'zrem'                      => 'Predis\Commands\ZSetRemove',
+            'zrange'                    => 'Predis\Commands\ZSetRange',
+            'zrevrange'                 => 'Predis\Commands\ZSetReverseRange',
+            'zrangebyscore'             => 'Predis\Commands\ZSetRangeByScore',
+            'zcard'                     => 'Predis\Commands\ZSetCardinality',
+            'zscore'                    => 'Predis\Commands\ZSetScore',
+            'zremrangebyscore'          => 'Predis\Commands\ZSetRemoveRangeByScore',
 
             /* connection related commands */
-            'ping'                      => '\Predis\Commands\ConnectionPing',
-            'auth'                      => '\Predis\Commands\ConnectionAuth',
-            'select'                    => '\Predis\Commands\ConnectionSelect',
-            'echo'                      => '\Predis\Commands\ConnectionEcho',
-            'quit'                      => '\Predis\Commands\ConnectionQuit',
+            'ping'                      => 'Predis\Commands\ConnectionPing',
+            'auth'                      => 'Predis\Commands\ConnectionAuth',
+            'select'                    => 'Predis\Commands\ConnectionSelect',
+            'echo'                      => 'Predis\Commands\ConnectionEcho',
+            'quit'                      => 'Predis\Commands\ConnectionQuit',
 
             /* remote server control commands */
-            'info'                      => '\Predis\Commands\ServerInfo',
-            'slaveof'                   => '\Predis\Commands\ServerSlaveOf',
-            'monitor'                   => '\Predis\Commands\ServerMonitor',
-            'dbsize'                    => '\Predis\Commands\ServerDatabaseSize',
-            'flushdb'                   => '\Predis\Commands\ServerFlushDatabase',
-            'flushall'                  => '\Predis\Commands\ServerFlushAll',
-            'save'                      => '\Predis\Commands\ServerSave',
-            'bgsave'                    => '\Predis\Commands\ServerBackgroundSave',
-            'lastsave'                  => '\Predis\Commands\ServerLastSave',
-            'shutdown'                  => '\Predis\Commands\ServerShutdown',
-            'bgrewriteaof'              => '\Predis\Commands\ServerBackgroundRewriteAOF',
+            'info'                      => 'Predis\Commands\ServerInfo',
+            'slaveof'                   => 'Predis\Commands\ServerSlaveOf',
+            'monitor'                   => 'Predis\Commands\ServerMonitor',
+            'dbsize'                    => 'Predis\Commands\ServerDatabaseSize',
+            'flushdb'                   => 'Predis\Commands\ServerFlushDatabase',
+            'flushall'                  => 'Predis\Commands\ServerFlushAll',
+            'save'                      => 'Predis\Commands\ServerSave',
+            'bgsave'                    => 'Predis\Commands\ServerBackgroundSave',
+            'lastsave'                  => 'Predis\Commands\ServerLastSave',
+            'shutdown'                  => 'Predis\Commands\ServerShutdown',
+            'bgrewriteaof'              => 'Predis\Commands\ServerBackgroundRewriteAOF',
 
 
             /* ---------------- Redis 2.0 ---------------- */
 
             /* commands operating on string values */
-            'setex'                     => '\Predis\Commands\StringSetExpire',
-            'append'                    => '\Predis\Commands\StringAppend',
-            'substr'                    => '\Predis\Commands\StringSubstr',
+            'setex'                     => 'Predis\Commands\StringSetExpire',
+            'append'                    => 'Predis\Commands\StringAppend',
+            'substr'                    => 'Predis\Commands\StringSubstr',
 
             /* commands operating on lists */
-            'blpop'                     => '\Predis\Commands\ListPopFirstBlocking',
-            'brpop'                     => '\Predis\Commands\ListPopLastBlocking',
+            'blpop'                     => 'Predis\Commands\ListPopFirstBlocking',
+            'brpop'                     => 'Predis\Commands\ListPopLastBlocking',
 
             /* commands operating on sorted sets */
-            'zunionstore'               => '\Predis\Commands\ZSetUnionStore',
-            'zinterstore'               => '\Predis\Commands\ZSetIntersectionStore',
-            'zcount'                    => '\Predis\Commands\ZSetCount',
-            'zrank'                     => '\Predis\Commands\ZSetRank',
-            'zrevrank'                  => '\Predis\Commands\ZSetReverseRank',
-            'zremrangebyrank'           => '\Predis\Commands\ZSetRemoveRangeByRank',
+            'zunionstore'               => 'Predis\Commands\ZSetUnionStore',
+            'zinterstore'               => 'Predis\Commands\ZSetIntersectionStore',
+            'zcount'                    => 'Predis\Commands\ZSetCount',
+            'zrank'                     => 'Predis\Commands\ZSetRank',
+            'zrevrank'                  => 'Predis\Commands\ZSetReverseRank',
+            'zremrangebyrank'           => 'Predis\Commands\ZSetRemoveRangeByRank',
 
             /* commands operating on hashes */
-            'hset'                      => '\Predis\Commands\HashSet',
-            'hsetnx'                    => '\Predis\Commands\HashSetPreserve',
-            'hmset'                     => '\Predis\Commands\HashSetMultiple',
-            'hincrby'                   => '\Predis\Commands\HashIncrementBy',
-            'hget'                      => '\Predis\Commands\HashGet',
-            'hmget'                     => '\Predis\Commands\HashGetMultiple',
-            'hdel'                      => '\Predis\Commands\HashDelete',
-            'hexists'                   => '\Predis\Commands\HashExists',
-            'hlen'                      => '\Predis\Commands\HashLength',
-            'hkeys'                     => '\Predis\Commands\HashKeys',
-            'hvals'                     => '\Predis\Commands\HashValues',
-            'hgetall'                   => '\Predis\Commands\HashGetAll',
+            'hset'                      => 'Predis\Commands\HashSet',
+            'hsetnx'                    => 'Predis\Commands\HashSetPreserve',
+            'hmset'                     => 'Predis\Commands\HashSetMultiple',
+            'hincrby'                   => 'Predis\Commands\HashIncrementBy',
+            'hget'                      => 'Predis\Commands\HashGet',
+            'hmget'                     => 'Predis\Commands\HashGetMultiple',
+            'hdel'                      => 'Predis\Commands\HashDelete',
+            'hexists'                   => 'Predis\Commands\HashExists',
+            'hlen'                      => 'Predis\Commands\HashLength',
+            'hkeys'                     => 'Predis\Commands\HashKeys',
+            'hvals'                     => 'Predis\Commands\HashValues',
+            'hgetall'                   => 'Predis\Commands\HashGetAll',
 
             /* transactions */
-            'multi'                     => '\Predis\Commands\TransactionMulti',
-            'exec'                      => '\Predis\Commands\TransactionExec',
-            'discard'                   => '\Predis\Commands\TransactionDiscard',
+            'multi'                     => 'Predis\Commands\TransactionMulti',
+            'exec'                      => 'Predis\Commands\TransactionExec',
+            'discard'                   => 'Predis\Commands\TransactionDiscard',
 
             /* publish - subscribe */
-            'subscribe'                 => '\Predis\Commands\PubSubSubscribe',
-            'unsubscribe'               => '\Predis\Commands\PubSubUnsubscribe',
-            'psubscribe'                => '\Predis\Commands\PubSubSubscribeByPattern',
-            'punsubscribe'              => '\Predis\Commands\PubSubUnsubscribeByPattern',
-            'publish'                   => '\Predis\Commands\PubSubPublish',
+            'subscribe'                 => 'Predis\Commands\PubSubSubscribe',
+            'unsubscribe'               => 'Predis\Commands\PubSubUnsubscribe',
+            'psubscribe'                => 'Predis\Commands\PubSubSubscribeByPattern',
+            'punsubscribe'              => 'Predis\Commands\PubSubUnsubscribeByPattern',
+            'publish'                   => 'Predis\Commands\PubSubPublish',
 
             /* remote server control commands */
-            'config'                    => '\Predis\Commands\ServerConfig',
+            'config'                    => 'Predis\Commands\ServerConfig',
 
 
             /* ---------------- Redis 2.2 ---------------- */
 
             /* commands operating on the key space */
-            'persist'                   => '\Predis\Commands\KeyPersist',
+            'persist'                   => 'Predis\Commands\KeyPersist',
 
             /* commands operating on string values */
-            'strlen'                    => '\Predis\Commands\StringStrlen',
-            'setrange'                  => '\Predis\Commands\StringSetRange',
-            'getrange'                  => '\Predis\Commands\StringGetRange',
-            'setbit'                    => '\Predis\Commands\StringSetBit',
-            'getbit'                    => '\Predis\Commands\StringGetBit',
+            'strlen'                    => 'Predis\Commands\StringStrlen',
+            'setrange'                  => 'Predis\Commands\StringSetRange',
+            'getrange'                  => 'Predis\Commands\StringGetRange',
+            'setbit'                    => 'Predis\Commands\StringSetBit',
+            'getbit'                    => 'Predis\Commands\StringGetBit',
 
             /* commands operating on lists */
-            'rpushx'                    => '\Predis\Commands\ListPushTailX',
-            'lpushx'                    => '\Predis\Commands\ListPushHeadX',
-            'linsert'                   => '\Predis\Commands\ListInsert',
-            'brpoplpush'                => '\Predis\Commands\ListPopLastPushHeadBlocking',
+            'rpushx'                    => 'Predis\Commands\ListPushTailX',
+            'lpushx'                    => 'Predis\Commands\ListPushHeadX',
+            'linsert'                   => 'Predis\Commands\ListInsert',
+            'brpoplpush'                => 'Predis\Commands\ListPopLastPushHeadBlocking',
 
             /* commands operating on sorted sets */
-            'zrevrangebyscore'          => '\Predis\Commands\ZSetReverseRangeByScore',
+            'zrevrangebyscore'          => 'Predis\Commands\ZSetReverseRangeByScore',
 
             /* transactions */
-            'watch'                     => '\Predis\Commands\TransactionWatch',
-            'unwatch'                   => '\Predis\Commands\TransactionUnwatch',
+            'watch'                     => 'Predis\Commands\TransactionWatch',
+            'unwatch'                   => 'Predis\Commands\TransactionUnwatch',
 
             /* remote server control commands */
-            'object'                    => '\Predis\Commands\ServerObject',
-            'slowlog'                   => '\Predis\Commands\ServerSlowlog',
+            'object'                    => 'Predis\Commands\ServerObject',
+            'slowlog'                   => 'Predis\Commands\ServerSlowlog',
         );
     }
 }

+ 121 - 121
lib/Predis/Profiles/ServerVersion24.php

@@ -35,176 +35,176 @@ class ServerVersion24 extends ServerProfile
             /* ---------------- Redis 1.2 ---------------- */
 
             /* commands operating on the key space */
-            'exists'                    => '\Predis\Commands\KeyExists',
-            'del'                       => '\Predis\Commands\KeyDelete',
-            'type'                      => '\Predis\Commands\KeyType',
-            'keys'                      => '\Predis\Commands\KeyKeys',
-            'randomkey'                 => '\Predis\Commands\KeyRandom',
-            'rename'                    => '\Predis\Commands\KeyRename',
-            'renamenx'                  => '\Predis\Commands\KeyRenamePreserve',
-            'expire'                    => '\Predis\Commands\KeyExpire',
-            'expireat'                  => '\Predis\Commands\KeyExpireAt',
-            'ttl'                       => '\Predis\Commands\KeyTimeToLive',
-            'move'                      => '\Predis\Commands\KeyMove',
-            'sort'                      => '\Predis\Commands\KeySort',
+            'exists'                    => 'Predis\Commands\KeyExists',
+            'del'                       => 'Predis\Commands\KeyDelete',
+            'type'                      => 'Predis\Commands\KeyType',
+            'keys'                      => 'Predis\Commands\KeyKeys',
+            'randomkey'                 => 'Predis\Commands\KeyRandom',
+            'rename'                    => 'Predis\Commands\KeyRename',
+            'renamenx'                  => 'Predis\Commands\KeyRenamePreserve',
+            'expire'                    => 'Predis\Commands\KeyExpire',
+            'expireat'                  => 'Predis\Commands\KeyExpireAt',
+            'ttl'                       => 'Predis\Commands\KeyTimeToLive',
+            'move'                      => 'Predis\Commands\KeyMove',
+            'sort'                      => 'Predis\Commands\KeySort',
 
             /* commands operating on string values */
-            'set'                       => '\Predis\Commands\StringSet',
-            'setnx'                     => '\Predis\Commands\StringSetPreserve',
-            'mset'                      => '\Predis\Commands\StringSetMultiple',
-            'msetnx'                    => '\Predis\Commands\StringSetMultiplePreserve',
-            'get'                       => '\Predis\Commands\StringGet',
-            'mget'                      => '\Predis\Commands\StringGetMultiple',
-            'getset'                    => '\Predis\Commands\StringGetSet',
-            'incr'                      => '\Predis\Commands\StringIncrement',
-            'incrby'                    => '\Predis\Commands\StringIncrementBy',
-            'decr'                      => '\Predis\Commands\StringDecrement',
-            'decrby'                    => '\Predis\Commands\StringDecrementBy',
+            'set'                       => 'Predis\Commands\StringSet',
+            'setnx'                     => 'Predis\Commands\StringSetPreserve',
+            'mset'                      => 'Predis\Commands\StringSetMultiple',
+            'msetnx'                    => 'Predis\Commands\StringSetMultiplePreserve',
+            'get'                       => 'Predis\Commands\StringGet',
+            'mget'                      => 'Predis\Commands\StringGetMultiple',
+            'getset'                    => 'Predis\Commands\StringGetSet',
+            'incr'                      => 'Predis\Commands\StringIncrement',
+            'incrby'                    => 'Predis\Commands\StringIncrementBy',
+            'decr'                      => 'Predis\Commands\StringDecrement',
+            'decrby'                    => 'Predis\Commands\StringDecrementBy',
 
             /* commands operating on lists */
-            'rpush'                     => '\Predis\Commands\ListPushTail',
-            'lpush'                     => '\Predis\Commands\ListPushHead',
-            'llen'                      => '\Predis\Commands\ListLength',
-            'lrange'                    => '\Predis\Commands\ListRange',
-            'ltrim'                     => '\Predis\Commands\ListTrim',
-            'lindex'                    => '\Predis\Commands\ListIndex',
-            'lset'                      => '\Predis\Commands\ListSet',
-            'lrem'                      => '\Predis\Commands\ListRemove',
-            'lpop'                      => '\Predis\Commands\ListPopFirst',
-            'rpop'                      => '\Predis\Commands\ListPopLast',
-            'rpoplpush'                 => '\Predis\Commands\ListPopLastPushHead',
+            'rpush'                     => 'Predis\Commands\ListPushTail',
+            'lpush'                     => 'Predis\Commands\ListPushHead',
+            'llen'                      => 'Predis\Commands\ListLength',
+            'lrange'                    => 'Predis\Commands\ListRange',
+            'ltrim'                     => 'Predis\Commands\ListTrim',
+            'lindex'                    => 'Predis\Commands\ListIndex',
+            'lset'                      => 'Predis\Commands\ListSet',
+            'lrem'                      => 'Predis\Commands\ListRemove',
+            'lpop'                      => 'Predis\Commands\ListPopFirst',
+            'rpop'                      => 'Predis\Commands\ListPopLast',
+            'rpoplpush'                 => 'Predis\Commands\ListPopLastPushHead',
 
             /* commands operating on sets */
-            'sadd'                      => '\Predis\Commands\SetAdd',
-            'srem'                      => '\Predis\Commands\SetRemove',
-            'spop'                      => '\Predis\Commands\SetPop',
-            'smove'                     => '\Predis\Commands\SetMove',
-            'scard'                     => '\Predis\Commands\SetCardinality',
-            'sismember'                 => '\Predis\Commands\SetIsMember',
-            'sinter'                    => '\Predis\Commands\SetIntersection',
-            'sinterstore'               => '\Predis\Commands\SetIntersectionStore',
-            'sunion'                    => '\Predis\Commands\SetUnion',
-            'sunionstore'               => '\Predis\Commands\SetUnionStore',
-            'sdiff'                     => '\Predis\Commands\SetDifference',
-            'sdiffstore'                => '\Predis\Commands\SetDifferenceStore',
-            'smembers'                  => '\Predis\Commands\SetMembers',
-            'srandmember'               => '\Predis\Commands\SetRandomMember',
+            'sadd'                      => 'Predis\Commands\SetAdd',
+            'srem'                      => 'Predis\Commands\SetRemove',
+            'spop'                      => 'Predis\Commands\SetPop',
+            'smove'                     => 'Predis\Commands\SetMove',
+            'scard'                     => 'Predis\Commands\SetCardinality',
+            'sismember'                 => 'Predis\Commands\SetIsMember',
+            'sinter'                    => 'Predis\Commands\SetIntersection',
+            'sinterstore'               => 'Predis\Commands\SetIntersectionStore',
+            'sunion'                    => 'Predis\Commands\SetUnion',
+            'sunionstore'               => 'Predis\Commands\SetUnionStore',
+            'sdiff'                     => 'Predis\Commands\SetDifference',
+            'sdiffstore'                => 'Predis\Commands\SetDifferenceStore',
+            'smembers'                  => 'Predis\Commands\SetMembers',
+            'srandmember'               => 'Predis\Commands\SetRandomMember',
 
             /* commands operating on sorted sets */
-            'zadd'                      => '\Predis\Commands\ZSetAdd',
-            'zincrby'                   => '\Predis\Commands\ZSetIncrementBy',
-            'zrem'                      => '\Predis\Commands\ZSetRemove',
-            'zrange'                    => '\Predis\Commands\ZSetRange',
-            'zrevrange'                 => '\Predis\Commands\ZSetReverseRange',
-            'zrangebyscore'             => '\Predis\Commands\ZSetRangeByScore',
-            'zcard'                     => '\Predis\Commands\ZSetCardinality',
-            'zscore'                    => '\Predis\Commands\ZSetScore',
-            'zremrangebyscore'          => '\Predis\Commands\ZSetRemoveRangeByScore',
+            'zadd'                      => 'Predis\Commands\ZSetAdd',
+            'zincrby'                   => 'Predis\Commands\ZSetIncrementBy',
+            'zrem'                      => 'Predis\Commands\ZSetRemove',
+            'zrange'                    => 'Predis\Commands\ZSetRange',
+            'zrevrange'                 => 'Predis\Commands\ZSetReverseRange',
+            'zrangebyscore'             => 'Predis\Commands\ZSetRangeByScore',
+            'zcard'                     => 'Predis\Commands\ZSetCardinality',
+            'zscore'                    => 'Predis\Commands\ZSetScore',
+            'zremrangebyscore'          => 'Predis\Commands\ZSetRemoveRangeByScore',
 
             /* connection related commands */
-            'ping'                      => '\Predis\Commands\ConnectionPing',
-            'auth'                      => '\Predis\Commands\ConnectionAuth',
-            'select'                    => '\Predis\Commands\ConnectionSelect',
-            'echo'                      => '\Predis\Commands\ConnectionEcho',
-            'quit'                      => '\Predis\Commands\ConnectionQuit',
+            'ping'                      => 'Predis\Commands\ConnectionPing',
+            'auth'                      => 'Predis\Commands\ConnectionAuth',
+            'select'                    => 'Predis\Commands\ConnectionSelect',
+            'echo'                      => 'Predis\Commands\ConnectionEcho',
+            'quit'                      => 'Predis\Commands\ConnectionQuit',
 
             /* remote server control commands */
-            'info'                      => '\Predis\Commands\ServerInfo',
-            'slaveof'                   => '\Predis\Commands\ServerSlaveOf',
-            'monitor'                   => '\Predis\Commands\ServerMonitor',
-            'dbsize'                    => '\Predis\Commands\ServerDatabaseSize',
-            'flushdb'                   => '\Predis\Commands\ServerFlushDatabase',
-            'flushall'                  => '\Predis\Commands\ServerFlushAll',
-            'save'                      => '\Predis\Commands\ServerSave',
-            'bgsave'                    => '\Predis\Commands\ServerBackgroundSave',
-            'lastsave'                  => '\Predis\Commands\ServerLastSave',
-            'shutdown'                  => '\Predis\Commands\ServerShutdown',
-            'bgrewriteaof'              => '\Predis\Commands\ServerBackgroundRewriteAOF',
+            'info'                      => 'Predis\Commands\ServerInfo',
+            'slaveof'                   => 'Predis\Commands\ServerSlaveOf',
+            'monitor'                   => 'Predis\Commands\ServerMonitor',
+            'dbsize'                    => 'Predis\Commands\ServerDatabaseSize',
+            'flushdb'                   => 'Predis\Commands\ServerFlushDatabase',
+            'flushall'                  => 'Predis\Commands\ServerFlushAll',
+            'save'                      => 'Predis\Commands\ServerSave',
+            'bgsave'                    => 'Predis\Commands\ServerBackgroundSave',
+            'lastsave'                  => 'Predis\Commands\ServerLastSave',
+            'shutdown'                  => 'Predis\Commands\ServerShutdown',
+            'bgrewriteaof'              => 'Predis\Commands\ServerBackgroundRewriteAOF',
 
 
             /* ---------------- Redis 2.0 ---------------- */
 
             /* commands operating on string values */
-            'setex'                     => '\Predis\Commands\StringSetExpire',
-            'append'                    => '\Predis\Commands\StringAppend',
-            'substr'                    => '\Predis\Commands\StringSubstr',
+            'setex'                     => 'Predis\Commands\StringSetExpire',
+            'append'                    => 'Predis\Commands\StringAppend',
+            'substr'                    => 'Predis\Commands\StringSubstr',
 
             /* commands operating on lists */
-            'blpop'                     => '\Predis\Commands\ListPopFirstBlocking',
-            'brpop'                     => '\Predis\Commands\ListPopLastBlocking',
+            'blpop'                     => 'Predis\Commands\ListPopFirstBlocking',
+            'brpop'                     => 'Predis\Commands\ListPopLastBlocking',
 
             /* commands operating on sorted sets */
-            'zunionstore'               => '\Predis\Commands\ZSetUnionStore',
-            'zinterstore'               => '\Predis\Commands\ZSetIntersectionStore',
-            'zcount'                    => '\Predis\Commands\ZSetCount',
-            'zrank'                     => '\Predis\Commands\ZSetRank',
-            'zrevrank'                  => '\Predis\Commands\ZSetReverseRank',
-            'zremrangebyrank'           => '\Predis\Commands\ZSetRemoveRangeByRank',
+            'zunionstore'               => 'Predis\Commands\ZSetUnionStore',
+            'zinterstore'               => 'Predis\Commands\ZSetIntersectionStore',
+            'zcount'                    => 'Predis\Commands\ZSetCount',
+            'zrank'                     => 'Predis\Commands\ZSetRank',
+            'zrevrank'                  => 'Predis\Commands\ZSetReverseRank',
+            'zremrangebyrank'           => 'Predis\Commands\ZSetRemoveRangeByRank',
 
             /* commands operating on hashes */
-            'hset'                      => '\Predis\Commands\HashSet',
-            'hsetnx'                    => '\Predis\Commands\HashSetPreserve',
-            'hmset'                     => '\Predis\Commands\HashSetMultiple',
-            'hincrby'                   => '\Predis\Commands\HashIncrementBy',
-            'hget'                      => '\Predis\Commands\HashGet',
-            'hmget'                     => '\Predis\Commands\HashGetMultiple',
-            'hdel'                      => '\Predis\Commands\HashDelete',
-            'hexists'                   => '\Predis\Commands\HashExists',
-            'hlen'                      => '\Predis\Commands\HashLength',
-            'hkeys'                     => '\Predis\Commands\HashKeys',
-            'hvals'                     => '\Predis\Commands\HashValues',
-            'hgetall'                   => '\Predis\Commands\HashGetAll',
+            'hset'                      => 'Predis\Commands\HashSet',
+            'hsetnx'                    => 'Predis\Commands\HashSetPreserve',
+            'hmset'                     => 'Predis\Commands\HashSetMultiple',
+            'hincrby'                   => 'Predis\Commands\HashIncrementBy',
+            'hget'                      => 'Predis\Commands\HashGet',
+            'hmget'                     => 'Predis\Commands\HashGetMultiple',
+            'hdel'                      => 'Predis\Commands\HashDelete',
+            'hexists'                   => 'Predis\Commands\HashExists',
+            'hlen'                      => 'Predis\Commands\HashLength',
+            'hkeys'                     => 'Predis\Commands\HashKeys',
+            'hvals'                     => 'Predis\Commands\HashValues',
+            'hgetall'                   => 'Predis\Commands\HashGetAll',
 
             /* transactions */
-            'multi'                     => '\Predis\Commands\TransactionMulti',
-            'exec'                      => '\Predis\Commands\TransactionExec',
-            'discard'                   => '\Predis\Commands\TransactionDiscard',
+            'multi'                     => 'Predis\Commands\TransactionMulti',
+            'exec'                      => 'Predis\Commands\TransactionExec',
+            'discard'                   => 'Predis\Commands\TransactionDiscard',
 
             /* publish - subscribe */
-            'subscribe'                 => '\Predis\Commands\PubSubSubscribe',
-            'unsubscribe'               => '\Predis\Commands\PubSubUnsubscribe',
-            'psubscribe'                => '\Predis\Commands\PubSubSubscribeByPattern',
-            'punsubscribe'              => '\Predis\Commands\PubSubUnsubscribeByPattern',
-            'publish'                   => '\Predis\Commands\PubSubPublish',
+            'subscribe'                 => 'Predis\Commands\PubSubSubscribe',
+            'unsubscribe'               => 'Predis\Commands\PubSubUnsubscribe',
+            'psubscribe'                => 'Predis\Commands\PubSubSubscribeByPattern',
+            'punsubscribe'              => 'Predis\Commands\PubSubUnsubscribeByPattern',
+            'publish'                   => 'Predis\Commands\PubSubPublish',
 
             /* remote server control commands */
-            'config'                    => '\Predis\Commands\ServerConfig',
+            'config'                    => 'Predis\Commands\ServerConfig',
 
 
             /* ---------------- Redis 2.2 ---------------- */
 
             /* commands operating on the key space */
-            'persist'                   => '\Predis\Commands\KeyPersist',
+            'persist'                   => 'Predis\Commands\KeyPersist',
 
             /* commands operating on string values */
-            'strlen'                    => '\Predis\Commands\StringStrlen',
-            'setrange'                  => '\Predis\Commands\StringSetRange',
-            'getrange'                  => '\Predis\Commands\StringGetRange',
-            'setbit'                    => '\Predis\Commands\StringSetBit',
-            'getbit'                    => '\Predis\Commands\StringGetBit',
+            'strlen'                    => 'Predis\Commands\StringStrlen',
+            'setrange'                  => 'Predis\Commands\StringSetRange',
+            'getrange'                  => 'Predis\Commands\StringGetRange',
+            'setbit'                    => 'Predis\Commands\StringSetBit',
+            'getbit'                    => 'Predis\Commands\StringGetBit',
 
             /* commands operating on lists */
-            'rpushx'                    => '\Predis\Commands\ListPushTailX',
-            'lpushx'                    => '\Predis\Commands\ListPushHeadX',
-            'linsert'                   => '\Predis\Commands\ListInsert',
-            'brpoplpush'                => '\Predis\Commands\ListPopLastPushHeadBlocking',
+            'rpushx'                    => 'Predis\Commands\ListPushTailX',
+            'lpushx'                    => 'Predis\Commands\ListPushHeadX',
+            'linsert'                   => 'Predis\Commands\ListInsert',
+            'brpoplpush'                => 'Predis\Commands\ListPopLastPushHeadBlocking',
 
             /* commands operating on sorted sets */
-            'zrevrangebyscore'          => '\Predis\Commands\ZSetReverseRangeByScore',
+            'zrevrangebyscore'          => 'Predis\Commands\ZSetReverseRangeByScore',
 
             /* transactions */
-            'watch'                     => '\Predis\Commands\TransactionWatch',
-            'unwatch'                   => '\Predis\Commands\TransactionUnwatch',
+            'watch'                     => 'Predis\Commands\TransactionWatch',
+            'unwatch'                   => 'Predis\Commands\TransactionUnwatch',
 
             /* remote server control commands */
-            'object'                    => '\Predis\Commands\ServerObject',
-            'slowlog'                   => '\Predis\Commands\ServerSlowlog',
+            'object'                    => 'Predis\Commands\ServerObject',
+            'slowlog'                   => 'Predis\Commands\ServerSlowlog',
 
 
             /* ---------------- Redis 2.4 ---------------- */
 
             /* remote server control commands */
-            'client'                    => '\Predis\Commands\ServerClient',
+            'client'                    => 'Predis\Commands\ServerClient',
         );
     }
 }

+ 10 - 10
lib/Predis/Profiles/ServerVersionNext.php

@@ -33,24 +33,24 @@ class ServerVersionNext extends ServerVersion24
     {
         return array_merge(parent::getSupportedCommands(), array(
             /* commands operating on the key space */
-            'pttl'                      => '\Predis\Commands\KeyPreciseTimeToLive',
-            'pexpire'                   => '\Predis\Commands\KeyPreciseExpire',
-            'pexpireat'                 => '\Predis\Commands\KeyPreciseExpireAt',
+            'pttl'                      => 'Predis\Commands\KeyPreciseTimeToLive',
+            'pexpire'                   => 'Predis\Commands\KeyPreciseExpire',
+            'pexpireat'                 => 'Predis\Commands\KeyPreciseExpireAt',
 
             /* commands operating on string values */
-            'psetex'                    => '\Predis\Commands\StringPreciseSetExpire',
-            'incrbyfloat'               => '\Predis\Commands\StringIncrementByFloat',
+            'psetex'                    => 'Predis\Commands\StringPreciseSetExpire',
+            'incrbyfloat'               => 'Predis\Commands\StringIncrementByFloat',
 
             /* commands operating on hashes */
-            'hincrbyfloat'              => '\Predis\Commands\HashIncrementByFloat',
+            'hincrbyfloat'              => 'Predis\Commands\HashIncrementByFloat',
 
             /* scripting */
-            'eval'                      => '\Predis\Commands\ServerEval',
-            'evalsha'                   => '\Predis\Commands\ServerEvalSHA',
-            'script'                    => '\Predis\Commands\ServerScript',
+            'eval'                      => 'Predis\Commands\ServerEval',
+            'evalsha'                   => 'Predis\Commands\ServerEvalSHA',
+            'script'                    => 'Predis\Commands\ServerScript',
 
             /* remote server control commands */
-            'info'                      => '\Predis\Commands\ServerInfoV26x',
+            'info'                      => 'Predis\Commands\ServerInfoV26x',
         ));
     }
 }

+ 1 - 2
lib/Predis/PubSub/DispatcherLoop.php

@@ -12,7 +12,6 @@
 namespace Predis\PubSub;
 
 use Predis\Client;
-use Predis\ClientException;
 
 /**
  * Method-dispatcher loop built around the client-side abstraction of a Redis
@@ -46,7 +45,7 @@ class DispatcherLoop
     protected function validateCallback($callable)
     {
         if (!is_callable($callable)) {
-            throw new ClientException("A valid callable object must be provided");
+            throw new \InvalidArgumentException("A valid callable object must be provided");
         }
     }
 

+ 24 - 14
lib/Predis/PubSub/PubSubContext.php

@@ -14,6 +14,7 @@ namespace Predis\PubSub;
 use Predis\Client;
 use Predis\Helpers;
 use Predis\ClientException;
+use Predis\NotSupportedException;
 
 /**
  * Client-side abstraction of a Publish / Subscribe context.
@@ -57,7 +58,7 @@ class PubSubContext implements \Iterator
      */
     public function __destruct()
     {
-        $this->closeContext();
+        $this->closeContext(true);
     }
 
     /**
@@ -69,17 +70,12 @@ class PubSubContext implements \Iterator
     private function checkCapabilities(Client $client)
     {
         if (Helpers::isCluster($client->getConnection())) {
-            throw new ClientException(
-                'Cannot initialize a PUB/SUB context over a cluster of connections'
-            );
+            throw new NotSupportedException('Cannot initialize a PUB/SUB context over a cluster of connections');
         }
 
         $commands = array('publish', 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe');
-
         if ($client->getProfile()->supportsCommands($commands) === false) {
-            throw new ClientException(
-                'The current profile does not support PUB/SUB related commands'
-            );
+            throw new NotSupportedException('The current profile does not support PUB/SUB related commands');
         }
     }
 
@@ -150,10 +146,23 @@ class PubSubContext implements \Iterator
 
     /**
      * Closes the context by unsubscribing from all the subscribed channels.
+     * Optionally, the context can be forcefully closed by dropping the
+     * underlying connection.
+     *
+     * @param Boolean $force Forcefully close the context by closing the connection.
+     * @return Boolean Returns false if there are no pending messages.
      */
-    public function closeContext()
+    public function closeContext($force = false)
     {
-        if ($this->valid()) {
+        if (!$this->valid()) {
+            return false;
+        }
+
+        if ($force) {
+            $this->invalidate();
+            $this->client->disconnect();
+        }
+        else {
             if ($this->isFlagSet(self::STATUS_SUBSCRIBED)) {
                 $this->unsubscribe();
             }
@@ -161,6 +170,8 @@ class PubSubContext implements \Iterator
                 $this->punsubscribe();
             }
         }
+
+        return !$force;
     }
 
     /**
@@ -208,7 +219,7 @@ class PubSubContext implements \Iterator
      */
     public function next()
     {
-        if ($this->isFlagSet(self::STATUS_VALID)) {
+        if ($this->valid()) {
             $this->position++;
         }
 
@@ -272,9 +283,8 @@ class PubSubContext implements \Iterator
                 );
 
             default:
-                throw new ClientException(
-                    "Received an unknown message type {$response[0]} inside of a pubsub context"
-                );
+                $message = "Received an unknown message type {$response[0]} inside of a pubsub context";
+                throw new ClientException($message);
         }
     }
 }

+ 9 - 23
lib/Predis/Transaction/MultiExecContext.php

@@ -16,6 +16,7 @@ use Predis\Helpers;
 use Predis\ResponseQueued;
 use Predis\ClientException;
 use Predis\ServerException;
+use Predis\NotSupportedException;
 use Predis\CommunicationException;
 use Predis\Protocol\ProtocolException;
 
@@ -112,17 +113,12 @@ class MultiExecContext
     private function checkCapabilities(Client $client)
     {
         if (Helpers::isCluster($client->getConnection())) {
-            throw new ClientException(
-                'Cannot initialize a MULTI/EXEC context over a cluster of connections'
-            );
+            throw new NotSupportedException('Cannot initialize a MULTI/EXEC context over a cluster of connections');
         }
 
         $profile = $client->getProfile();
-
         if ($profile->supportsCommands(array('multi', 'exec', 'discard')) === false) {
-            throw new ClientException(
-                'The current profile does not support MULTI, EXEC and DISCARD'
-            );
+            throw new NotSupportedException('The current profile does not support MULTI, EXEC and DISCARD');
         }
 
         $this->canWatch = $profile->supportsCommands(array('watch', 'unwatch'));
@@ -134,9 +130,7 @@ class MultiExecContext
     private function isWatchSupported()
     {
         if ($this->canWatch === false) {
-            throw new ClientException(
-                'The current profile does not support WATCH and UNWATCH'
-            );
+            throw new NotSupportedException('The current profile does not support WATCH and UNWATCH');
         }
     }
 
@@ -256,7 +250,7 @@ class MultiExecContext
     {
         $this->isWatchSupported();
         $this->unflagState(self::STATE_WATCH);
-        $this->client->unwatch();
+        $this->__call('unwatch', array());
 
         return $this;
     }
@@ -297,31 +291,23 @@ class MultiExecContext
     private function checkBeforeExecution($callable)
     {
         if ($this->checkState(self::STATE_INSIDEBLOCK)) {
-            throw new ClientException(
-                "Cannot invoke 'execute' or 'exec' inside an active client transaction block"
-            );
+            throw new ClientException("Cannot invoke 'execute' or 'exec' inside an active client transaction block");
         }
 
         if ($callable) {
             if (!is_callable($callable)) {
-                throw new \InvalidArgumentException(
-                    'Argument passed must be a callable object'
-                );
+                throw new \InvalidArgumentException('Argument passed must be a callable object');
             }
 
             if (count($this->commands) > 0) {
                 $this->discard();
-                throw new ClientException(
-                    'Cannot execute a transaction block after using fluent interface'
-                );
+                throw new ClientException('Cannot execute a transaction block after using fluent interface');
             }
         }
 
         if (isset($this->options['retry']) && !isset($callable)) {
             $this->discard();
-            throw new \InvalidArgumentException(
-                'Automatic retries can be used only when a transaction block is provided'
-            );
+            throw new \InvalidArgumentException('Automatic retries can be used only when a transaction block is provided');
         }
     }
 

+ 23 - 6
phpunit.xml.dist

@@ -1,12 +1,24 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<phpunit bootstrap="test/bootstrap.php" colors="true" strict="true">
+<phpunit bootstrap="tests/bootstrap.php" colors="true" strict="true">
     <testsuites>
         <testsuite name="Predis Test Suite">
-            <directory>test/</directory>
+            <directory>tests/Predis/</directory>
         </testsuite>
     </testsuites>
 
+    <groups>
+        <exclude>
+            <group>ext-phpiredis</group>
+            <group>ext-curl</group>
+            <group>realm-network-webdis</group>
+            <!-- <group>connected</group> -->
+            <!-- <group>disconnected</group> -->
+            <!-- <group>commands</group> -->
+            <!-- <group>slow</group> -->
+        </exclude>
+    </groups>
+
     <filter>
         <whitelist>
             <directory suffix=".php">lib/Predis/</directory>
@@ -14,9 +26,14 @@
     </filter>
 
     <php>
-        <const name="TEST_SERVER_VERSION" value="2.2" />
-        <const name="TEST_SERVER_HOST" value="127.0.0.1" />
-        <const name="TEST_SERVER_PORT" value="6379" />
-        <const name="TEST_SERVER_DBNUM" value="15" />
+        <!-- Redis -->
+        <const name="REDIS_SERVER_VERSION" value="2.4" />
+        <const name="REDIS_SERVER_HOST" value="127.0.0.1" />
+        <const name="REDIS_SERVER_PORT" value="6379" />
+        <const name="REDIS_SERVER_DBNUM" value="15" />
+
+        <!-- Webdis -->
+        <const name="WEBDIS_SERVER_HOST" value="127.0.0.1" />
+        <const name="WEBDIS_SERVER_PORT" value="7379" />
     </php>
 </phpunit>

+ 0 - 830
test/ClientFeaturesTest.php

@@ -1,830 +0,0 @@
-<?php
-
-/*
- * This file is part of the Predis package.
- *
- * (c) Daniele Alessandri <suppakilla@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-class ClientFeaturesTestSuite extends PHPUnit_Framework_TestCase
-{
-    public $redis;
-
-    protected function setUp()
-    {
-        $this->redis = RC::getConnection();
-        $this->redis->flushdb();
-    }
-
-    protected function tearDown()
-    {
-    }
-
-    protected function onNotSuccessfulTest(Exception $exception)
-    {
-        // drops and reconnect to a redis server on uncaught exceptions
-        RC::resetConnection();
-
-        parent::onNotSuccessfulTest($exception);
-    }
-
-    /* Predis\ConnectionParameters */
-
-    function testConnectionParametersDefaultValues()
-    {
-        $params = new Predis\ConnectionParameters();
-
-        $this->assertEquals('127.0.0.1', $params->host);
-        $this->assertEquals(6379, $params->port);
-        $this->assertEquals(5, $params->connection_timeout);
-        $this->assertNull($params->read_write_timeout);
-        $this->assertNull($params->database);
-        $this->assertNull($params->password);
-        $this->assertNull($params->alias);
-    }
-
-    function testConnectionParametersSetupValuesArray()
-    {
-        $paramsArray = RC::getConnectionParametersArgumentsArray();
-        $params = new Predis\ConnectionParameters($paramsArray);
-
-        $this->assertEquals($paramsArray['host'], $params->host);
-        $this->assertEquals($paramsArray['port'], $params->port);
-        $this->assertEquals($paramsArray['connection_timeout'], $params->connection_timeout);
-        $this->assertEquals($paramsArray['read_write_timeout'], $params->read_write_timeout);
-        $this->assertEquals($paramsArray['database'], $params->database);
-        $this->assertEquals($paramsArray['password'], $params->password);
-        $this->assertEquals($paramsArray['alias'], $params->alias);
-    }
-
-    function testConnectionParametersSetupValuesString()
-    {
-        $paramsArray  = RC::getConnectionParametersArgumentsArray();
-        $paramsString = RC::getConnectionParametersArgumentsString($paramsArray);
-        $params = new Predis\ConnectionParameters($paramsArray);
-
-        $this->assertEquals($paramsArray['host'], $params->host);
-        $this->assertEquals($paramsArray['port'], $params->port);
-        $this->assertEquals($paramsArray['connection_timeout'], $params->connection_timeout);
-        $this->assertEquals($paramsArray['read_write_timeout'], $params->read_write_timeout);
-        $this->assertEquals($paramsArray['database'], $params->database);
-        $this->assertEquals($paramsArray['password'], $params->password);
-        $this->assertEquals($paramsArray['alias'], $params->alias);
-    }
-
-    /* Predis\Commands\Command and derivates */
-
-    function testCommand_TestArguments()
-    {
-        $cmdArgs = array('key1', 'key2', 'key3');
-
-        $cmd = new Predis\Commands\StringGetMultiple();
-        $cmd->setArguments($cmdArgs);
-        $this->assertEquals($cmdArgs[0], $cmd->getArgument(0));
-        $this->assertEquals($cmdArgs[1], $cmd->getArgument(1));
-        $this->assertEquals($cmdArgs[2], $cmd->getArgument(2));
-
-        $cmd = new Predis\Commands\ConnectionPing();
-        $this->assertNull($cmd->getArgument(0));
-    }
-
-    function testCommand_ParseResponse()
-    {
-        // default parser
-        $cmd = new Predis\Commands\StringGet();
-        $this->assertEquals('test', $cmd->parseResponse('test'));
-
-        // overridden parser (boolean)
-        $cmd = new Predis\Commands\KeyExists();
-        $this->assertTrue($cmd->parseResponse('1'));
-        $this->assertFalse($cmd->parseResponse('0'));
-
-        // overridden parser (boolean)
-        $cmd = new Predis\Commands\ConnectionPing();
-        $this->assertTrue($cmd->parseResponse('PONG'));
-
-        // overridden parser (complex)
-        // TODO: emulate a respons to INFO
-    }
-
-
-    /* Predis\Profiles\ServerProfile and derivates */
-
-    function testServerProfile_GetSpecificVersions()
-    {
-        $this->assertInstanceOf('\Predis\Profiles\ServerVersion12', Predis\Profiles\ServerProfile::get('1.2'));
-        $this->assertInstanceOf('\Predis\Profiles\ServerVersion20', Predis\Profiles\ServerProfile::get('2.0'));
-        $this->assertInstanceOf('\Predis\Profiles\ServerVersion22', Predis\Profiles\ServerProfile::get('2.2'));
-        $this->assertInstanceOf('\Predis\Profiles\ServerVersionNext', Predis\Profiles\ServerProfile::get('dev'));
-        $this->assertInstanceOf('\Predis\Profiles\ServerProfile', Predis\Profiles\ServerProfile::get('default'));
-        $this->assertEquals(Predis\Profiles\ServerProfile::get('default'), Predis\Profiles\ServerProfile::getDefault());
-    }
-
-    function testServerProfile_SupportedCommands()
-    {
-        $profile_12 = Predis\Profiles\ServerProfile::get('1.2');
-        $profile_20 = Predis\Profiles\ServerProfile::get('2.0');
-
-        $this->assertTrue($profile_12->supportsCommand('info'));
-        $this->assertTrue($profile_20->supportsCommand('info'));
-
-        $this->assertFalse($profile_12->supportsCommand('multi'));
-        $this->assertTrue($profile_20->supportsCommand('multi'));
-
-        $this->assertFalse($profile_12->supportsCommand('watch'));
-        $this->assertFalse($profile_20->supportsCommand('watch'));
-    }
-
-    function testServerProfile_CommandsCreation()
-    {
-        $profile = Predis\Profiles\ServerProfile::get('2.0');
-
-        $cmdNoArgs = $profile->createCommand('info');
-        $this->assertInstanceOf('\Predis\Commands\ServerInfo', $cmdNoArgs);
-        $this->assertNull($cmdNoArgs->getArgument());
-
-        $args = array('key1', 'key2');
-        $cmdWithArgs = $profile->createCommand('mget', $args);
-
-        $this->assertInstanceOf('\Predis\Commands\StringGetMultiple', $cmdWithArgs);
-        $this->assertEquals($args[0], $cmdWithArgs->getArgument()); // TODO: why?
-        $this->assertEquals($args[0], $cmdWithArgs->getArgument(0));
-        $this->assertEquals($args[1], $cmdWithArgs->getArgument(1));
-
-        $bogusCommand    = 'not_existing_command';
-        $expectedMessage = "'$bogusCommand' is not a registered Redis command";
-        RC::testForClientException($this, $expectedMessage, function()
-            use($profile, $bogusCommand) {
-
-            $profile->createCommand($bogusCommand);
-        });
-    }
-
-    function testServerProfile_CommandsRegistration()
-    {
-        $profile  = Predis\Profiles\ServerProfile::get('1.2');
-        $cmdId    = 'multi';
-        $cmdClass = '\Predis\Commands\TransactionMulti';
-
-        $this->assertFalse($profile->supportsCommand($cmdId));
-        $profile->defineCommand($cmdId, new $cmdClass());
-        $this->assertTrue($profile->supportsCommand($cmdId));
-        $this->assertInstanceOf($cmdClass, $profile->createCommand($cmdId));
-    }
-
-    function testServerProfile_CaseInsensitiveCommandsNames() {
-        $profile = $this->redis->getProfile();
-
-        $uppercase = $profile->supportsCommand('INFO');
-        $lowercase = $profile->supportsCommand('info');
-        $this->assertEquals($uppercase, $lowercase);
-
-        $uppercase = $profile->createCommand('INFO');
-        $lowercase = $profile->createCommand('info');
-        $this->assertEquals($uppercase, $lowercase);
-    }
-
-    /* Predis\ResponseQueued */
-
-    function testResponseQueued()
-    {
-        $response = new Predis\ResponseQueued();
-        $this->assertInstanceOf('\Predis\IReplyObject', $response);
-        $this->assertEquals(\Predis\Protocol\Text\TextProtocol::QUEUED, (string)$response);
-    }
-
-    /* Predis\ResponseError */
-
-    function testResponseError()
-    {
-        $errorMessage = 'ERROR MESSAGE';
-        $response = new Predis\ResponseError($errorMessage);
-
-        $this->assertInstanceOf('\Predis\IReplyObject', $response);
-        $this->assertInstanceOf('\Predis\IRedisServerError', $response);
-
-        $this->assertEquals($errorMessage, $response->getMessage());
-        $this->assertEquals($errorMessage, (string)$response);
-    }
-
-    /* Predis\Network\StreamConnection */
-
-    function testStreamConnection_StringCastReturnsIPAndPort()
-    {
-        $connection = new Predis\Network\StreamConnection(RC::getConnectionParameters());
-        $this->assertEquals(RC::SERVER_HOST . ':' . RC::SERVER_PORT, (string) $connection);
-    }
-
-    function testStreamConnection_ConnectDisconnect()
-    {
-        $connection = new Predis\Network\StreamConnection(RC::getConnectionParameters());
-
-        $this->assertFalse($connection->isConnected());
-        $connection->connect();
-        $this->assertTrue($connection->isConnected());
-        $connection->disconnect();
-        $this->assertFalse($connection->isConnected());
-    }
-
-    function testStreamConnection_WriteAndReadCommand()
-    {
-        $cmd = Predis\Profiles\ServerProfile::getDefault()->createCommand('ping');
-        $connection = new Predis\Network\StreamConnection(RC::getConnectionParameters());
-        $connection->connect();
-
-        $connection->writeCommand($cmd);
-        $this->assertTrue($connection->readResponse($cmd));
-    }
-
-    function testStreamConnection_WriteCommandAndCloseConnection()
-    {
-        $cmd = Predis\Profiles\ServerProfile::getDefault()->createCommand('quit');
-        $connection = new Predis\Network\StreamConnection(new Predis\ConnectionParameters(
-            RC::getConnectionArguments() + array('read_write_timeout' => 0.5)
-        ));
-
-        $connection->connect();
-
-        $this->assertTrue($connection->isConnected());
-
-        $connection->writeCommand($cmd);
-        $connection->disconnect();
-
-        $exceptionMessage = 'Error while reading line from the server';
-        RC::testForCommunicationException($this, $exceptionMessage, function() use($connection, $cmd) {
-            $connection->readResponse($cmd);
-        });
-    }
-
-    function testStreamConnection_GetSocketOpensConnection()
-    {
-        $connection = new Predis\Network\StreamConnection(RC::getConnectionParameters());
-
-        $this->assertFalse($connection->isConnected());
-        $this->assertInternalType('resource', $connection->getResource());
-        $this->assertTrue($connection->isConnected());
-    }
-
-    function testStreamConnection_LazyConnect()
-    {
-        $cmd = Predis\Profiles\ServerProfile::getDefault()->createCommand('ping');
-        $connection = new Predis\Network\StreamConnection(RC::getConnectionParameters());
-
-        $this->assertFalse($connection->isConnected());
-
-        $connection->writeCommand($cmd);
-
-        $this->assertTrue($connection->isConnected());
-        $this->assertTrue($connection->readResponse($cmd));
-    }
-
-    function testStreamConnection_Alias()
-    {
-        $connection1 = new Predis\Network\StreamConnection(RC::getConnectionParameters());
-        $this->assertNull($connection1->getParameters()->alias);
-
-        $args = array_merge(RC::getConnectionArguments(), array('alias' => 'servername'));
-        $connection2 = new Predis\Network\StreamConnection(new Predis\ConnectionParameters($args));
-
-        $this->assertEquals('servername', $connection2->getParameters()->alias);
-    }
-
-    function testStreamConnection_ConnectionTimeout()
-    {
-        $timeout = 3;
-        $args = array('host' => '1.0.0.1', 'connection_timeout' => $timeout);
-        $connection = new Predis\Network\StreamConnection(new Predis\ConnectionParameters($args));
-
-        $start = time();
-        RC::testForCommunicationException($this, null, function() use($connection) {
-            $connection->connect();
-        });
-
-        $this->assertEquals((float)(time() - $start), $timeout, '', 1);
-    }
-
-    function testStreamConnection_ReadTimeout()
-    {
-        $timeout = 1;
-        $args = array_merge(RC::getConnectionArguments(), array('read_write_timeout' => $timeout));
-        $cmdFake = Predis\Profiles\ServerProfile::getDefault()->createCommand('ping');
-        $connection = new Predis\Network\StreamConnection(new Predis\ConnectionParameters($args));
-
-        $expectedMessage = 'Error while reading line from the server';
-        $start = time();
-        RC::testForCommunicationException($this, $expectedMessage, function() use($connection, $cmdFake) {
-            $connection->readResponse($cmdFake);
-        });
-        $this->assertEquals((float)(time() - $start), $timeout, '', 1);
-    }
-
-    /* Predis\Protocol\TextResponseReader */
-
-    function testResponseReader_OptionIterableMultiBulkReplies()
-    {
-        $protocol = new Predis\Protocol\Text\ComposableTextProtocol();
-        $reader = $protocol->getReader();
-        $connection = new Predis\Network\ComposableStreamConnection(RC::getConnectionParameters(), $protocol);
-
-        $reader->setHandler(
-            Predis\Protocol\Text\TextProtocol::PREFIX_MULTI_BULK,
-            new Predis\Protocol\Text\ResponseMultiBulkHandler()
-        );
-        $connection->writeBytes("KEYS *\r\n");
-        $this->assertInternalType('array', $reader->read($connection));
-
-        $reader->setHandler(
-            Predis\Protocol\Text\TextProtocol::PREFIX_MULTI_BULK,
-            new Predis\Protocol\Text\ResponseMultiBulkStreamHandler()
-        );
-        $connection->writeBytes("KEYS *\r\n");
-        $this->assertInstanceOf('\Iterator', $reader->read($connection));
-    }
-
-    function testResponseReader_OptionExceptionOnError()
-    {
-        $protocol = new Predis\Protocol\Text\ComposableTextProtocol();
-        $reader = $protocol->getReader();
-        $connection = new Predis\Network\ComposableStreamConnection(RC::getConnectionParameters(), $protocol);
-
-        $rawCmdUnexpected = "*3\r\n$5\r\nLPUSH\r\n$3\r\nkey\r\n$5\r\nvalue\r\n";
-        $connection->writeBytes("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n");
-        $reader->read($connection);
-
-        $reader->setHandler(
-            Predis\Protocol\Text\TextProtocol::PREFIX_ERROR,
-            new Predis\Protocol\Text\ResponseErrorSilentHandler()
-        );
-        $connection->writeBytes($rawCmdUnexpected);
-        $errorReply = $reader->read($connection);
-        $this->assertInstanceOf('\Predis\ResponseError', $errorReply);
-        $this->assertEquals(RC::EXCEPTION_WRONG_TYPE, $errorReply->getMessage());
-
-        $reader->setHandler(
-            Predis\Protocol\Text\TextProtocol::PREFIX_ERROR,
-            new Predis\Protocol\Text\ResponseErrorHandler()
-        );
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function()
-            use ($connection, $rawCmdUnexpected) {
-
-            $connection->writeBytes($rawCmdUnexpected);
-            $connection->getProtocol()->read($connection);
-        });
-    }
-
-    function testResponseReader_EmptyBulkResponse()
-    {
-        $protocol = new Predis\Protocol\Text\ComposableTextProtocol();
-        $connection = new Predis\Network\ComposableStreamConnection(RC::getConnectionParameters(), $protocol);
-        $client = new Predis\Client($connection);
-
-        $this->assertTrue($client->set('foo', ''));
-        $this->assertEquals('', $client->get('foo'));
-        $this->assertEquals('', $client->get('foo'));
-    }
-
-    /* Client initialization */
-
-    function testClientInitialization_SingleConnectionParameters()
-    {
-        $params1 = array_merge(RC::getConnectionArguments(), array(
-            'connection_timeout' => 10,
-            'read_write_timeout' => 30,
-            'alias' => 'connection_alias',
-        ));
-        $params2 = RC::getConnectionParametersArgumentsString($params1);
-        $params3 = new Predis\ConnectionParameters($params1);
-        $params4 = new Predis\Network\StreamConnection($params3);
-
-        foreach (array($params1, $params2, $params3, $params4) as $params) {
-            $client = new Predis\Client($params);
-            $parameters = $client->getConnection()->getParameters();
-            $this->assertEquals($params1['host'], $parameters->host);
-            $this->assertEquals($params1['port'], $parameters->port);
-            $this->assertEquals($params1['connection_timeout'], $parameters->connection_timeout);
-            $this->assertEquals($params1['read_write_timeout'], $parameters->read_write_timeout);
-            $this->assertEquals($params1['alias'], $parameters->alias);
-            $this->assertNull($parameters->password);
-        }
-    }
-
-    function testClientInitialization_ClusterConnectionParameters()
-    {
-        $params1 = array_merge(RC::getConnectionArguments(), array(
-            'connection_timeout' => 10,
-            'read_write_timeout' => 30,
-        ));
-        $params2 = RC::getConnectionParametersArgumentsString($params1);
-        $params3 = new Predis\ConnectionParameters($params1);
-        $params4 = new Predis\Network\StreamConnection($params3);
-
-        $client1 = new Predis\Client(array($params1, $params2, $params3, $params4));
-
-        foreach ($client1->getConnection() as $connection) {
-            $parameters = $connection->getParameters();
-            $this->assertEquals($params1['host'], $parameters->host);
-            $this->assertEquals($params1['port'], $parameters->port);
-            $this->assertEquals($params1['connection_timeout'], $parameters->connection_timeout);
-            $this->assertEquals($params1['read_write_timeout'], $parameters->read_write_timeout);
-            $this->assertNull($parameters->password);
-        }
-
-        $connectionCluster = $client1->getConnection();
-        $client2 = new Predis\Client($connectionCluster);
-        $this->assertSame($connectionCluster, $client2->getConnection());
-    }
-
-    /* Client + PipelineContext */
-
-    function testPipelineContext_Simple()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $pipe = $client->pipeline();
-        $pipelineClass = '\Predis\Pipeline\PipelineContext';
-
-        $this->assertInstanceOf($pipelineClass, $pipe);
-        $this->assertInstanceOf($pipelineClass, $pipe->set('foo', 'bar'));
-        $this->assertInstanceOf($pipelineClass, $pipe->set('hoge', 'piyo'));
-        $this->assertInstanceOf($pipelineClass, $pipe->mset(array(
-            'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
-        )));
-        $this->assertInstanceOf($pipelineClass, $pipe->mget(array(
-            'foo', 'hoge', 'foofoo', 'hogehoge'
-        )));
-
-        $replies = $pipe->execute();
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals(4, count($replies));
-        $this->assertEquals(4, count($replies[3]));
-        $this->assertEquals('barbar', $replies[3][2]);
-    }
-
-    function testPipelineContext_FluentInterface()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $replies = $client->pipeline()->ping()->set('foo', 'bar')->get('foo')->execute();
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals('bar', $replies[2]);
-    }
-
-    function testPipelineContext_CallableAnonymousBlock()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $replies = $client->pipeline(function($pipe) {
-            $pipe->ping();
-            $pipe->set('foo', 'bar');
-            $pipe->get('foo');
-        });
-
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals('bar', $replies[2]);
-    }
-
-    function testPipelineContext_ClientExceptionInCallableBlock()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        RC::testForClientException($this, 'TEST', function() use($client) {
-            $client->pipeline(function($pipe) {
-                $pipe->ping();
-                $pipe->set('foo', 'bar');
-                throw new Predis\ClientException("TEST");
-            });
-        });
-
-        $this->assertFalse($client->exists('foo'));
-    }
-
-    function testPipelineContext_ServerExceptionInCallableBlock()
-    {
-        $client = RC::createConnection(array('throw_errors' => false));
-        $client->flushdb();
-
-        $replies = $client->pipeline(function($pipe) {
-            $pipe->set('foo', 'bar');
-            $pipe->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
-            $pipe->set('hoge', 'piyo');
-        });
-
-        $this->assertInternalType('array', $replies);
-        $this->assertInstanceOf('\Predis\ResponseError', $replies[1]);
-        $this->assertTrue($client->exists('foo'));
-        $this->assertTrue($client->exists('hoge'));
-    }
-
-    function testPipelineContext_Flush()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $pipe = $client->pipeline();
-        $pipe->set('foo', 'bar')->set('hoge', 'piyo');
-        $pipe->flushPipeline();
-        $pipe->ping()->mget(array('foo', 'hoge'));
-        $replies = $pipe->execute();
-
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals(4, count($replies));
-        $this->assertEquals('bar', $replies[3][0]);
-        $this->assertEquals('piyo', $replies[3][1]);
-    }
-
-    /* Predis\Client + Predis\MultiExecContext  */
-
-    function testMultiExecContext_Simple()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $multi = $client->multiExec();
-        $transactionClass = '\Predis\Transaction\MultiExecContext';
-
-        $this->assertInstanceOf($transactionClass, $multi);
-        $this->assertInstanceOf($transactionClass, $multi->set('foo', 'bar'));
-        $this->assertInstanceOf($transactionClass, $multi->set('hoge', 'piyo'));
-        $this->assertInstanceOf($transactionClass, $multi->mset(array(
-            'foofoo' => 'barbar', 'hogehoge' => 'piyopiyo'
-        )));
-        $this->assertInstanceOf($transactionClass, $multi->mget(array(
-            'foo', 'hoge', 'foofoo', 'hogehoge'
-        )));
-
-        $replies = $multi->execute();
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals(4, count($replies));
-        $this->assertEquals(4, count($replies[3]));
-        $this->assertEquals('barbar', $replies[3][2]);
-    }
-
-    function testMultiExecContext_FluentInterface()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $replies = $client->multiExec()->ping()->set('foo', 'bar')->get('foo')->execute();
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals('bar', $replies[2]);
-    }
-
-    function testMultiExecContext_CallableAnonymousBlock()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $replies = $client->multiExec(function($multi) {
-            $multi->ping();
-            $multi->set('foo', 'bar');
-            $multi->get('foo');
-        });
-
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals('bar', $replies[2]);
-    }
-
-    /**
-     * @expectedException Predis\ClientException
-     */
-    function testMultiExecContext_CannotMixFluentInterfaceAndAnonymousBlock()
-    {
-        $emptyBlock = function($tx) { };
-        $tx = RC::getConnection()->multiExec()->get('foo')->execute($emptyBlock);
-    }
-
-    function testMultiExecContext_EmptyCallableBlock()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $replies = $client->multiExec(function($multi) { });
-        $this->assertEquals(0, count($replies));
-
-        $options = array('cas' => true);
-        $replies = $client->multiExec($options, function($multi) { });
-        $this->assertEquals(0, count($replies));
-
-        $options = array('cas' => true);
-        $replies = $client->multiExec($options, function($multi) {
-            $multi->multi();
-        });
-        $this->assertEquals(0, count($replies));
-    }
-
-    function testMultiExecContext_ClientExceptionInCallableBlock()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        RC::testForClientException($this, 'TEST', function() use($client) {
-            $client->multiExec(function($multi) {
-                $multi->ping();
-                $multi->set('foo', 'bar');
-                throw new Predis\ClientException("TEST");
-            });
-        });
-
-        $this->assertFalse($client->exists('foo'));
-    }
-
-    function testMultiExecContext_ServerExceptionInCallableBlock()
-    {
-        $client = RC::createConnection(array('throw_errors' => false));
-        $client->flushdb();
-
-        $replies = $client->multiExec(function($multi) {
-            $multi->set('foo', 'bar');
-            $multi->lpush('foo', 'piyo'); // LIST operation on STRING type returns an ERROR
-            $multi->set('hoge', 'piyo');
-        });
-
-        $this->assertInternalType('array', $replies);
-        $this->assertInstanceOf('\Predis\ResponseError', $replies[1]);
-        $this->assertTrue($client->exists('foo'));
-        $this->assertTrue($client->exists('hoge'));
-    }
-
-    function testMultiExecContext_Discard()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $replies = $client->multiExec(function($multi) {
-            $multi->set('foo', 'bar');
-            $multi->discard();
-            $multi->set('hoge', 'piyo');
-        });
-
-        $this->assertEquals(1, count($replies));
-        $this->assertFalse($client->exists('foo'));
-        $this->assertTrue($client->exists('hoge'));
-    }
-
-    function testMultiExecContext_DiscardEmpty()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $replies = $client->multiExec(function($multi) {
-            $multi->discard();
-        });
-
-        $this->assertEquals(0, count($replies));
-    }
-
-    function testMultiExecContext_Watch()
-    {
-        $client1 = RC::getConnection();
-        $client2 = RC::getConnection(true);
-        $client1->flushdb();
-
-        RC::testForAbortedMultiExecException($this, function()
-            use($client1, $client2) {
-
-            $client1->multiExec(array('watch' => 'sentinel'), function($multi)
-                use ($client2) {
-
-                $multi->set('sentinel', 'client1');
-                $multi->get('sentinel');
-                $client2->set('sentinel', 'client2');
-            });
-        });
-
-        $this->assertEquals('client2', $client1->get('sentinel'));
-    }
-
-    function testMultiExecContext_CheckAndSet()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-        $client->set('foo', 'bar');
-
-        $options = array('watch' => 'foo', 'cas' => true);
-        $replies = $client->multiExec($options, function($tx) {
-            $tx->watch('foobar');
-            $foo = $tx->get('foo');
-            $tx->multi();
-            $tx->set('foobar', $foo);
-            $tx->mget('foo', 'foobar');
-        });
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals(array(true, array('bar', 'bar')), $replies);
-
-        $tx = $client->multiExec($options);
-        $tx->watch('foobar');
-        $foo = $tx->get('foo');
-        $replies = $tx->multi()
-                      ->set('foobar', $foo)
-                      ->mget('foo', 'foobar')
-                      ->execute();
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals(array(true, array('bar', 'bar')), $replies);
-    }
-
-    function testMultiExecContext_RetryOnServerAbort()
-    {
-        $client1 = RC::getConnection();
-        $client2 = RC::getConnection(true);
-        $client1->flushdb();
-
-        $retry = 3;
-        $attempts = 0;
-        RC::testForAbortedMultiExecException($this, function()
-            use($client1, $client2, $retry, &$attempts) {
-
-            $options = array('watch' => 'sentinel', 'retry' => $retry);
-            $client1->multiExec($options, function($tx)
-                use ($client2, &$attempts) {
-
-                $attempts++;
-                $tx->set('sentinel', 'client1');
-                $tx->get('sentinel');
-                $client2->set('sentinel', 'client2');
-            });
-        });
-        $this->assertEquals('client2', $client1->get('sentinel'));
-        $this->assertEquals($retry + 1, $attempts);
-
-        $retry = 3;
-        $attempts = 0;
-        RC::testForAbortedMultiExecException($this, function()
-            use($client1, $client2, $retry, &$attempts) {
-
-            $options = array(
-                'watch' => 'sentinel',
-                'cas'   => true,
-                'retry' => $retry
-            );
-            $client1->multiExec($options, function($tx)
-                use ($client2, &$attempts) {
-
-                $attempts++;
-                $tx->incr('attempts');
-                $tx->multi();
-                $tx->set('sentinel', 'client1');
-                $tx->get('sentinel');
-                $client2->set('sentinel', 'client2');
-            });
-        });
-        $this->assertEquals('client2', $client1->get('sentinel'));
-        $this->assertEquals($retry + 1, $attempts);
-        $this->assertEquals($attempts, $client1->get('attempts'));
-    }
-
-    /**
-     * @expectedException InvalidArgumentException
-     */
-    function testMultiExecContext_RetryNotAvailableWithoutBlock()
-    {
-        $options = array('watch' => 'foo', 'retry' => 1);
-        $tx = RC::getConnection()->multiExec($options);
-        $tx->multi()->get('foo')->exec();
-    }
-
-    function testMultiExecContext_CheckAndSet_Discard()
-    {
-        $client = RC::getConnection();
-        $client->flushdb();
-
-        $client->set('foo', 'bar');
-        $options = array('watch' => 'foo', 'cas' => true);
-        $replies = $client->multiExec($options, function($tx) {
-            $tx->watch('foobar');
-            $foo = $tx->get('foo');
-            $tx->multi();
-            $tx->set('foobar', $foo);
-            $tx->discard();
-            $tx->mget('foo', 'foobar');
-        });
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals(array(array('bar', null)), $replies);
-
-        $hijack = true;
-        $client->set('foo', 'bar');
-        $client2 = RC::getConnection(true);
-        $options = array('watch' => 'foo', 'cas' => true, 'retry' => 1);
-        $replies = $client->multiExec($options, function($tx)
-            use ($client2, &$hijack) {
-
-            $foo = $tx->get('foo');
-            $tx->multi();
-            $tx->set('foobar', $foo);
-            $tx->discard();
-            if ($hijack) {
-                $hijack = false;
-                $client2->set('foo', 'hijacked!');
-            }
-            $tx->mget('foo', 'foobar');
-        });
-        $this->assertInternalType('array', $replies);
-        $this->assertEquals(array(array('hijacked!', null)), $replies);
-    }
-}

+ 0 - 283
test/PredisShared.php

@@ -1,283 +0,0 @@
-<?php
-
-/*
- * This file is part of the Predis package.
- *
- * (c) Daniele Alessandri <suppakilla@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-if (!file_exists(__DIR__.'/enable.tests')) {
-    exit(
-        "Please create an empty file named 'enable.tests' inside the test directory ".
-        "in order to proceed.\n\n*** DO NOT *** run this test suite against servers in ".
-        "a production environment or containing data you are interested in!\n"
-    );
-}
-
-if (!function_exists('array_union')) {
-    function array_union(Array $a, Array $b)
-    {
-        return array_merge($a, array_diff($b, $a));
-    }
-}
-
-class RC
-{
-    const SERVER_VERSION   = TEST_SERVER_VERSION;
-    const SERVER_HOST      = TEST_SERVER_HOST;
-    const SERVER_PORT      = TEST_SERVER_PORT;
-    const DEFAULT_DATABASE = TEST_SERVER_DBNUM;
-
-    const WIPE_OUT         = 1;
-    const EXCEPTION_WRONG_TYPE     = 'ERR Operation against a key holding the wrong kind of value';
-    const EXCEPTION_NO_SUCH_KEY    = 'ERR no such key';
-    const EXCEPTION_OUT_OF_RANGE   = 'ERR index out of range';
-    const EXCEPTION_OFFSET_RANGE   = 'ERR offset is out of range';
-    const EXCEPTION_INVALID_DB_IDX = 'ERR invalid DB index';
-    const EXCEPTION_VALUE_NOT_INT  = 'ERR value is not an integer';
-    const EXCEPTION_EXEC_NO_MULTI  = 'ERR EXEC without MULTI';
-    const EXCEPTION_SETEX_TTL      = 'ERR invalid expire time in SETEX';
-    const EXCEPTION_HASH_VALNOTINT = 'ERR hash value is not an integer';
-    const EXCEPTION_BIT_VALUE      = 'ERR bit is not an integer or out of range';
-    const EXCEPTION_BIT_OFFSET     = 'ERR bit offset is not an integer or out of range';
-
-    private static $connection;
-
-    public static function getConnectionArguments(Array $additional = array())
-    {
-        return array_merge(array('host' => RC::SERVER_HOST, 'port' => RC::SERVER_PORT), $additional);
-    }
-
-    public static function getConnectionParameters(Array $additional = array())
-    {
-        return new Predis\ConnectionParameters(self::getConnectionArguments($additional));
-    }
-
-    public static function createConnection(Array $additional = array())
-    {
-        $serverProfile = Predis\Profiles\ServerProfile::get(self::SERVER_VERSION);
-
-        $connection = new Predis\Client(RC::getConnectionArguments($additional), $serverProfile);
-        $connection->connect();
-        $connection->select(RC::DEFAULT_DATABASE);
-
-        return $connection;
-    }
-
-    public static function getConnection($new = false)
-    {
-        if ($new == true) {
-            return self::createConnection();
-        }
-
-        if (self::$connection === null || !self::$connection->isConnected()) {
-            self::$connection = self::createConnection();
-        }
-
-        return self::$connection;
-    }
-
-    public static function resetConnection()
-    {
-        if (self::$connection !== null && self::$connection->isConnected()) {
-            self::$connection->disconnect();
-            self::$connection = self::createConnection();
-        }
-    }
-
-    public static function helperForBlockingPops($op)
-    {
-        // TODO: I admit that this helper is kinda lame and it does not run
-        //       in a separate process to properly test BLPOP/BRPOP
-        $redisUri = sprintf('tcp://%s:%d/?database=%d', RC::SERVER_HOST, RC::SERVER_PORT, RC::DEFAULT_DATABASE);
-        $handle = popen('php', 'w');
-
-        fwrite($handle, "<?php
-        require __DIR__.'/test/bootstrap.php';
-        \$redis = new Predis\Client('$redisUri');
-        \$redis->rpush('{$op}1', 'a');
-        \$redis->rpush('{$op}2', 'b');
-        \$redis->rpush('{$op}3', 'c');
-        \$redis->rpush('{$op}1', 'd');
-        ?>");
-
-        pclose($handle);
-    }
-
-    public static function getArrayOfNumbers()
-    {
-        return array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
-    }
-
-    public static function getKeyValueArray()
-    {
-        return array(
-            'foo'      => 'bar',
-            'hoge'     => 'piyo',
-            'foofoo'   => 'barbar',
-        );
-    }
-
-    public static function getNamespacedKeyValueArray()
-    {
-        return array(
-            'metavar:foo'      => 'bar',
-            'metavar:hoge'     => 'piyo',
-            'metavar:foofoo'   => 'barbar',
-        );
-    }
-
-    public static function getZSetArray()
-    {
-        return array('a' => -10, 'b' => 0, 'c' => 10, 'd' => 20, 'e' => 20, 'f' => 30);
-    }
-
-    public static function sameValuesInArrays($arrayA, $arrayB)
-    {
-        if (count($arrayA) != count($arrayB)) {
-            return false;
-        }
-
-        return count(array_diff($arrayA, $arrayB)) == 0;
-    }
-
-    public static function testForServerException($testcaseInstance, $expectedMessage, $wrapFunction)
-    {
-        $thrownException = null;
-
-        try {
-            $wrapFunction($testcaseInstance);
-        }
-        catch (Predis\ServerException $exception) {
-            $thrownException = $exception;
-        }
-
-        $testcaseInstance->assertInstanceOf('Predis\ServerException', $thrownException);
-
-        if (isset($expectedMessage)) {
-            $testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
-        }
-    }
-
-    public static function testForClientException($testcaseInstance, $expectedMessage, $wrapFunction)
-    {
-        $thrownException = null;
-
-        try {
-            $wrapFunction($testcaseInstance);
-        }
-        catch (Predis\ClientException $exception) {
-            $thrownException = $exception;
-        }
-
-        $testcaseInstance->assertInstanceOf('Predis\ClientException', $thrownException);
-
-        if (isset($expectedMessage)) {
-            $testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
-        }
-    }
-
-    public static function testForCommunicationException($testcaseInstance, $expectedMessage, $wrapFunction)
-    {
-        $thrownException = null;
-
-        try {
-            $wrapFunction($testcaseInstance);
-        }
-        catch (Predis\CommunicationException $exception) {
-            $thrownException = $exception;
-        }
-
-        $testcaseInstance->assertInstanceOf('Predis\CommunicationException', $thrownException);
-
-        if (isset($expectedMessage)) {
-            $testcaseInstance->assertEquals($expectedMessage, $thrownException->getMessage());
-        }
-    }
-
-    public static function testForAbortedMultiExecException($testcaseInstance, $wrapFunction)
-    {
-        $thrownException = null;
-
-        try {
-            $wrapFunction($testcaseInstance);
-        }
-        catch (Predis\Transaction\AbortedMultiExecException $exception) {
-            $thrownException = $exception;
-        }
-
-        $testcaseInstance->assertInstanceOf('Predis\Transaction\AbortedMultiExecException', $thrownException);
-    }
-
-    public static function pushTailAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0)
-    {
-        if ($wipeOut == true) {
-            $client->del($keyName);
-        }
-
-        foreach ($values as $value) {
-            $client->rpush($keyName, $value);
-        }
-
-        return $values;
-    }
-
-    public static function setAddAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0)
-    {
-        if ($wipeOut == true) {
-            $client->del($keyName);
-        }
-
-        foreach ($values as $value) {
-            $client->sadd($keyName, $value);
-        }
-
-        return $values;
-    }
-
-    public static function zsetAddAndReturn(Predis\Client $client, $keyName, Array $values, $wipeOut = 0)
-    {
-        // $values: array(SCORE => VALUE, ...);
-        if ($wipeOut == true) {
-            $client->del($keyName);
-        }
-
-        foreach ($values as $value => $score) {
-            $client->zadd($keyName, $score, $value);
-        }
-
-        return $values;
-    }
-
-    public static function getConnectionParametersArgumentsArray()
-    {
-        return array(
-            'host' => '10.0.0.1',
-            'port' => 6380,
-            'connection_timeout' => 10,
-            'read_write_timeout' => 30,
-            'database' => 5,
-            'password' => 'dbpassword',
-            'alias' => 'connection_alias',
-        );
-    }
-
-    public static function getConnectionParametersArgumentsString($arguments = null)
-    {
-        // TODO: must be improved
-        $args = $arguments ?: RC::getConnectionParametersArgumentsArray();
-        $paramsString = "tcp://{$args['host']}:{$args['port']}/?";
-
-        unset($args['host']);
-        unset($args['port']);
-
-        foreach($args as $k => $v) {
-            $paramsString .= "$k=$v&";
-        }
-
-        return $paramsString;
-    }
-}

+ 0 - 2181
test/RedisCommandsTest.php

@@ -1,2181 +0,0 @@
-<?php
-
-/*
- * This file is part of the Predis package.
- *
- * (c) Daniele Alessandri <suppakilla@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-class RedisCommandTestSuite extends PHPUnit_Framework_TestCase
-{
-    public $redis;
-
-    // TODO: instead of an boolean assertion against the return value
-    //       of RC::sameValuesInArrays, we should extend PHPUnit with
-    //       a new assertion, e.g. $this->assertSameValues();
-    // TODO: an option to skip certain tests such as testflushdbs
-    //       should be provided.
-    // TODO: missing test with float values for a few commands
-
-    protected function setUp()
-    {
-        $this->redis = RC::getConnection();
-        $this->redis->flushdb();
-    }
-
-    protected function tearDown()
-    {
-    }
-
-    protected function onNotSuccessfulTest(Exception $exception)
-    {
-        // drops and reconnect to a redis server on uncaught exceptions
-        RC::resetConnection();
-
-        parent::onNotSuccessfulTest($exception);
-    }
-
-    /* miscellaneous commands */
-
-    function testPing()
-    {
-        $this->assertTrue($this->redis->ping());
-    }
-
-    function testEcho()
-    {
-        $string = 'This is an echo test!';
-        $this->assertEquals($string, $this->redis->echo($string));
-    }
-
-    function testQuit()
-    {
-        $this->redis->quit();
-        $this->assertFalse($this->redis->isConnected());
-    }
-
-    function testMultiExec()
-    {
-        // NOTE: due to a limitation in the current implementation of Predis\Client,
-        //       the replies returned by Predis\Commands\Exec are not parsed by their
-        //       respective Predis\Commands\Command::parseResponse methods. If you
-        //       need that kind of behaviour, you should use an instance of
-        //       Predis\MultiExecBlock.
-
-        $this->assertTrue($this->redis->multi());
-        $this->assertInstanceOf('Predis\ResponseQueued', $this->redis->ping());
-        $this->assertInstanceOf('Predis\ResponseQueued', $this->redis->echo('hello'));
-        $this->assertInstanceOf('Predis\ResponseQueued', $this->redis->echo('redis'));
-        $this->assertEquals(array('PONG', 'hello', 'redis'), $this->redis->exec());
-
-        $this->assertTrue($this->redis->multi());
-        $this->assertEquals(array(), $this->redis->exec());
-
-        // should throw an exception when trying to EXEC without having previously issued MULTI
-        RC::testForServerException($this, RC::EXCEPTION_EXEC_NO_MULTI, function($test) {
-            $test->redis->exec();
-        });
-    }
-
-    function testDiscard()
-    {
-        $this->assertTrue($this->redis->multi());
-        $this->assertInstanceOf('Predis\ResponseQueued', $this->redis->set('foo', 'bar'));
-        $this->assertInstanceOf('Predis\ResponseQueued', $this->redis->set('hoge', 'piyo'));
-        $this->assertEquals(true, $this->redis->discard());
-
-        // should throw an exception when trying to EXEC after a DISCARD
-        RC::testForServerException($this, RC::EXCEPTION_EXEC_NO_MULTI, function($test) {
-            $test->redis->exec();
-        });
-
-        $this->assertFalse($this->redis->exists('foo'));
-        $this->assertFalse($this->redis->exists('hoge'));
-    }
-
-    /* commands operating on string values */
-
-    function testSet()
-    {
-        $this->assertTrue($this->redis->set('foo', 'bar'));
-        $this->assertEquals('bar', $this->redis->get('foo'));
-    }
-
-    function testGet()
-    {
-        $this->redis->set('foo', 'bar');
-        $this->assertEquals('bar', $this->redis->get('foo'));
-
-        $this->assertTrue($this->redis->set('foo', ''));
-        $this->assertEquals('', $this->redis->get('foo'));
-
-        $this->assertNull($this->redis->get('fooDoesNotExist'));
-
-        // should throw an exception when trying to do a GET on non-string types
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->rpush('metavars', 'foo');
-            $test->redis->get('metavars');
-        });
-    }
-
-    function testExists()
-    {
-        $this->redis->set('foo', 'bar');
-
-        $this->assertTrue($this->redis->exists('foo'));
-        $this->assertFalse($this->redis->exists('key_does_not_exist'));
-    }
-
-    function testSetPreserve()
-    {
-        $multi = RC::getKeyValueArray();
-
-        $this->assertTrue($this->redis->setnx('foo', 'bar'));
-        $this->assertFalse($this->redis->setnx('foo', 'rab'));
-        $this->assertEquals('bar', $this->redis->get('foo'));
-    }
-
-    function testMultipleSetAndGet()
-    {
-        $multi = RC::getKeyValueArray();
-
-        // key=>value pairs via array instance
-        $this->assertTrue($this->redis->mset($multi));
-        $multiRet = $this->redis->mget(array_keys($multi));
-        $this->assertEquals($multi, array_combine(array_keys($multi), array_values($multiRet)));
-
-        // key=>value pairs via function arguments
-        $this->assertTrue($this->redis->mset('a', 1, 'b', 2, 'c', 3));
-        $this->assertEquals(array(1, 2, 3), $this->redis->mget('a', 'b', 'c'));
-    }
-
-    function testSetMultiplePreserve()
-    {
-        $multi    = RC::getKeyValueArray();
-        $newpair  = array('hogehoge' => 'piyopiyo');
-        $hijacked = array('foo' => 'baz', 'hoge' => 'fuga');
-
-        // successful set
-        $expectedResult = array_merge($multi, $newpair);
-        $this->redis->mset($multi);
-        $this->assertTrue($this->redis->msetnx($newpair));
-        $this->assertEquals(
-            array_values($expectedResult),
-            $this->redis->mget(array_keys($expectedResult))
-        );
-
-        $this->redis->flushdb();
-
-        // unsuccessful set
-        $expectedResult = array_merge($multi, array('hogehoge' => null));
-        $this->redis->mset($multi);
-        $this->assertFalse($this->redis->msetnx(array_merge($newpair, $hijacked)));
-        $this->assertEquals(
-            array_values($expectedResult),
-            $this->redis->mget(array_keys($expectedResult))
-        );
-    }
-
-    function testGetSet()
-    {
-        $this->assertNull($this->redis->getset('foo', 'bar'));
-        $this->assertEquals('bar', $this->redis->getset('foo', 'barbar'));
-        $this->assertEquals('barbar', $this->redis->getset('foo', 'baz'));
-    }
-
-    function testIncrementAndIncrementBy()
-    {
-        // test subsequent increment commands
-        $this->assertEquals(1, $this->redis->incr('foo'));
-        $this->assertEquals(2, $this->redis->incr('foo'));
-
-        // test subsequent incrementBy commands
-        $this->assertEquals(22, $this->redis->incrby('foo', 20));
-        $this->assertEquals(10, $this->redis->incrby('foo', -12));
-        $this->assertEquals(-100, $this->redis->incrby('foo', -110));
-    }
-
-    function testDecrementAndDecrementBy()
-    {
-        // test subsequent decrement commands
-        $this->assertEquals(-1, $this->redis->decr('foo'));
-        $this->assertEquals(-2, $this->redis->decr('foo'));
-
-        // test subsequent decrementBy commands
-        $this->assertEquals(-22, $this->redis->decrby('foo', 20));
-        $this->assertEquals(-10, $this->redis->decrby('foo', -12));
-        $this->assertEquals(100, $this->redis->decrby('foo', -110));
-    }
-
-    function testDelete()
-    {
-        $this->redis->set('foo', 'bar');
-        $this->assertEquals(1, $this->redis->del('foo'));
-        $this->assertFalse($this->redis->exists('foo'));
-        $this->assertEquals(0, $this->redis->del('foo'));
-    }
-
-    function testType()
-    {
-        $this->assertEquals('none', $this->redis->type('fooDoesNotExist'));
-
-        $this->redis->set('fooString', 'bar');
-        $this->assertEquals('string', $this->redis->type('fooString'));
-
-        $this->redis->rpush('fooList', 'bar');
-        $this->assertEquals('list', $this->redis->type('fooList'));
-
-        $this->redis->sadd('fooSet', 'bar');
-        $this->assertEquals('set', $this->redis->type('fooSet'));
-
-        $this->redis->zadd('fooZSet', 0, 'bar');
-        $this->assertEquals('zset', $this->redis->type('fooZSet'));
-
-        $this->redis->hset('fooHash', 'value', 'bar');
-        $this->assertEquals('hash', $this->redis->type('fooHash'));
-    }
-
-    function testAppend()
-    {
-        $this->redis->set('foo', 'bar');
-        $this->assertEquals(5, $this->redis->append('foo', '__'));
-        $this->assertEquals(8, $this->redis->append('foo', 'bar'));
-        $this->assertEquals('bar__bar', $this->redis->get('foo'));
-
-        $this->assertEquals(4, $this->redis->append('hoge', 'piyo'));
-        $this->assertEquals('piyo', $this->redis->get('hoge'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->rpush('metavars', 'foo');
-            $test->redis->append('metavars', 'bar');
-        });
-    }
-
-    function testSetRange()
-    {
-        $this->assertEquals(6, $this->redis->setrange('var', 0, 'foobar'));
-        $this->assertEquals('foobar', $this->redis->get('var'));
-        $this->assertEquals(6, $this->redis->setrange('var', 3, 'foo'));
-        $this->assertEquals('foofoo', $this->redis->get('var'));
-        $this->assertEquals(16, $this->redis->setrange('var', 10, 'barbar'));
-        $this->assertEquals("foofoo\x00\x00\x00\x00barbar", $this->redis->get('var'));
-
-        $this->assertEquals(4, $this->redis->setrange('binary', 0, pack('l', -2147483648)));
-        list($unpacked) = array_values(unpack('l', $this->redis->get('binary')));
-        $this->assertEquals(-2147483648, $unpacked);
-
-        RC::testForServerException($this, RC::EXCEPTION_OFFSET_RANGE, function($test) {
-            $test->redis->setrange('var', -1, 'bogus');
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->rpush('metavars', 'foo');
-            $test->redis->setrange('metavars', 0, 'hoge');
-        });
-    }
-
-    function testSubstr()
-    {
-        $this->redis->set('var', 'foobar');
-        $this->assertEquals('foo', $this->redis->substr('var', 0, 2));
-        $this->assertEquals('bar', $this->redis->substr('var', 3, 5));
-        $this->assertEquals('bar', $this->redis->substr('var', -3, -1));
-
-        $this->assertEquals('', $this->redis->substr('var', 5, 0));
-
-        $this->redis->set('numeric', 123456789);
-        $this->assertEquals(12345, $this->redis->substr('numeric', 0, 4));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->rpush('metavars', 'foo');
-            $test->redis->substr('metavars', 0, 3);
-        });
-    }
-
-    function testStrlen()
-    {
-        $this->redis->set('var', 'foobar');
-        $this->assertEquals(6, $this->redis->strlen('var'));
-        $this->assertEquals(9, $this->redis->append('var', '___'));
-        $this->assertEquals(9, $this->redis->strlen('var'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->rpush('metavars', 'foo');
-            $test->redis->strlen('metavars');
-        });
-    }
-
-    function testSetBit()
-    {
-        $this->assertEquals(0, $this->redis->setbit('binary', 31, 1));
-        $this->assertEquals(0, $this->redis->setbit('binary', 0, 1));
-        $this->assertEquals(4, $this->redis->strlen('binary'));
-        $this->assertEquals("\x80\x00\00\x01", $this->redis->get('binary'));
-
-        $this->assertEquals(1, $this->redis->setbit('binary', 0, 0));
-        $this->assertEquals(0, $this->redis->setbit('binary', 0, 0));
-        $this->assertEquals("\x00\x00\00\x01", $this->redis->get('binary'));
-
-        RC::testForServerException($this, RC::EXCEPTION_BIT_OFFSET, function($test) {
-            $test->redis->setbit('binary', -1, 1);
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_BIT_OFFSET, function($test) {
-            $test->redis->setbit('binary', 'invalid', 1);
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_BIT_VALUE, function($test) {
-            $test->redis->setbit('binary', 15, 255);
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_BIT_VALUE, function($test) {
-            $test->redis->setbit('binary', 15, 'invalid');
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->rpush('metavars', 'foo');
-            $test->redis->setbit('metavars', 0, 1);
-        });
-    }
-
-    function testGetBit()
-    {
-        $this->redis->set('binary', "\x80\x00\00\x01");
-
-        $this->assertEquals(1, $this->redis->getbit('binary', 0));
-        $this->assertEquals(0, $this->redis->getbit('binary', 15));
-        $this->assertEquals(1, $this->redis->getbit('binary', 31));
-        $this->assertEquals(0, $this->redis->getbit('binary', 63));
-
-        RC::testForServerException($this, RC::EXCEPTION_BIT_OFFSET, function($test) {
-            $test->redis->getbit('binary', -1);
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_BIT_OFFSET, function($test) {
-            $test->redis->getbit('binary', 'invalid');
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->rpush('metavars', 'foo');
-            $test->redis->getbit('metavars', 0);
-        });
-    }
-
-    /* commands operating on the key space */
-
-    function testKeys()
-    {
-        $keyValsNs     = RC::getNamespacedKeyValueArray();
-        $keyValsOthers = array('aaa' => 1, 'aba' => 2, 'aca' => 3);
-        $allKeyVals    = array_merge($keyValsNs, $keyValsOthers);
-
-        $this->redis->mset($allKeyVals);
-
-        $this->assertEquals(array(), $this->redis->keys('nokeys:*'));
-
-        $keysFromRedis = $this->redis->keys('metavar:*');
-        $this->assertEquals(array(), array_diff(array_keys($keyValsNs), $keysFromRedis));
-
-        $keysFromRedis = $this->redis->keys('*');
-        $this->assertEquals(array(), array_diff(array_keys($allKeyVals), $keysFromRedis));
-
-        $keysFromRedis = $this->redis->keys('a?a');
-        $this->assertEquals(array(), array_diff(array_keys($keyValsOthers), $keysFromRedis));
-    }
-
-    function testRandomKey()
-    {
-        $keyvals = RC::getKeyValueArray();
-
-        $this->assertNull($this->redis->randomkey());
-
-        $this->redis->mset($keyvals);
-        $this->assertTrue(in_array($this->redis->randomkey(), array_keys($keyvals)));
-    }
-
-    function testRename()
-    {
-        $this->redis->mset(array('foo' => 'bar', 'foofoo' => 'barbar'));
-
-        // rename existing keys
-        $this->assertTrue($this->redis->rename('foo', 'foofoo'));
-        $this->assertFalse($this->redis->exists('foo'));
-        $this->assertEquals('bar', $this->redis->get('foofoo'));
-
-        // should throw an excepion then trying to rename non-existing keys
-        RC::testForServerException($this, RC::EXCEPTION_NO_SUCH_KEY, function($test) {
-            $test->redis->rename('hoge', 'hogehoge');
-        });
-    }
-
-    function testRenamePreserve()
-    {
-        $this->redis->mset(array('foo' => 'bar', 'hoge' => 'piyo', 'hogehoge' => 'piyopiyo'));
-
-        $this->assertTrue($this->redis->renamenx('foo', 'foofoo'));
-        $this->assertFalse($this->redis->exists('foo'));
-        $this->assertEquals('bar', $this->redis->get('foofoo'));
-
-        $this->assertFalse($this->redis->renamenx('hoge', 'hogehoge'));
-        $this->assertTrue($this->redis->exists('hoge'));
-
-        // should throw an excepion then trying to rename non-existing keys
-        RC::testForServerException($this, RC::EXCEPTION_NO_SUCH_KEY, function($test) {
-            $test->redis->renamenx('fuga', 'baz');
-        });
-    }
-
-    function testExpirationAndTTL()
-    {
-        $this->redis->set('foo', 'bar');
-
-        // check for key expiration
-        $this->assertTrue($this->redis->expire('foo', 1));
-        $this->assertEquals(1, $this->redis->ttl('foo'));
-        $this->assertTrue($this->redis->exists('foo'));
-        sleep(2);
-        $this->assertFalse($this->redis->exists('foo'));
-        $this->assertEquals(-1, $this->redis->ttl('foo'));
-
-        // check for consistent TTL values
-        $this->redis->set('foo', 'bar');
-        $this->assertTrue($this->redis->expire('foo', 100));
-        sleep(3);
-        $this->assertEquals(97, $this->redis->ttl('foo'));
-
-        // delete key on negative TTL
-        $this->redis->set('foo', 'bar');
-        $this->assertTrue($this->redis->expire('foo', -100));
-        $this->assertFalse($this->redis->exists('foo'));
-        $this->assertEquals(-1, $this->redis->ttl('foo'));
-    }
-
-    function testPersist()
-    {
-        $this->redis->set('foo', 'bar');
-
-        $this->assertTrue($this->redis->expire('foo', 1));
-        $this->assertEquals(1, $this->redis->ttl('foo'));
-        $this->assertTrue($this->redis->persist('foo'));
-        $this->assertEquals(-1, $this->redis->ttl('foo'));
-
-        $this->assertFalse($this->redis->persist('foo'));
-        $this->assertFalse($this->redis->persist('foobar'));
-    }
-
-    function testSetExpire()
-    {
-        $this->assertTrue($this->redis->setex('foo', 10, 'bar'));
-        $this->assertTrue($this->redis->exists('foo'));
-        $this->assertEquals(10, $this->redis->ttl('foo'));
-
-        $this->assertTrue($this->redis->setex('hoge', 1, 'piyo'));
-        sleep(2);
-        $this->assertFalse($this->redis->exists('hoge'));
-
-        // TODO: do not check the error message RC::EXCEPTION_VALUE_NOT_INT for now
-        RC::testForServerException($this, null, function($test) {
-            $test->redis->setex('hoge', 2.5, 'piyo');
-        });
-        RC::testForServerException($this, RC::EXCEPTION_SETEX_TTL, function($test) {
-            $test->redis->setex('hoge', 0, 'piyo');
-        });
-        RC::testForServerException($this, RC::EXCEPTION_SETEX_TTL, function($test) {
-            $test->redis->setex('hoge', -10, 'piyo');
-        });
-    }
-
-    function testDatabaseSize()
-    {
-        // TODO: is this really OK?
-        $this->assertEquals(0, $this->redis->dbsize());
-        $this->redis->mset(RC::getKeyValueArray());
-        $this->assertGreaterThan(0, $this->redis->dbsize());
-    }
-
-    /* commands operating on lists */
-
-    function testPushTail()
-    {
-        // NOTE: List push operations return the list length since Redis commit 520b5a3
-        $this->assertEquals(1, $this->redis->rpush('metavars', 'foo'));
-        $this->assertTrue($this->redis->exists('metavars'));
-        $this->assertEquals(2, $this->redis->rpush('metavars', 'hoge'));
-
-        // should throw an exception when trying to do a RPUSH on non-list types
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->rpush('foo', 'bar');
-        });
-    }
-
-    function testPushTailX()
-    {
-        $this->assertEquals(0, $this->redis->rpushx('numbers', 1));
-        $this->assertEquals(1, $this->redis->rpush('numbers', 2));
-        $this->assertEquals(2, $this->redis->rpushx('numbers', 3));
-
-        $this->assertEquals(2, $this->redis->llen('numbers'));
-        $this->assertEquals(array(2, 3), $this->redis->lrange('numbers', 0, -1));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->rpushx('foo', 'bar');
-        });
-    }
-
-    function testPushHead()
-    {
-        // NOTE: List push operations return the list length since Redis commit 520b5a3
-        $this->assertEquals(1, $this->redis->lpush('metavars', 'foo'));
-        $this->assertTrue($this->redis->exists('metavars'));
-        $this->assertEquals(2, $this->redis->lpush('metavars', 'hoge'));
-
-        // should throw an exception when trying to do a LPUSH on non-list types
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->lpush('foo', 'bar');
-        });
-    }
-
-    function testPushHeadX()
-    {
-        $this->assertEquals(0, $this->redis->lpushx('numbers', 1));
-        $this->assertEquals(1, $this->redis->lpush('numbers', 2));
-        $this->assertEquals(2, $this->redis->lpushx('numbers', 3));
-
-        $this->assertEquals(2, $this->redis->llen('numbers'));
-        $this->assertEquals(array(3, 2), $this->redis->lrange('numbers', 0, -1));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->lpushx('foo', 'bar');
-        });
-    }
-
-    function testListLength()
-    {
-        $this->assertEquals(1, $this->redis->rpush('metavars', 'foo'));
-        $this->assertEquals(2, $this->redis->rpush('metavars', 'hoge'));
-        $this->assertEquals(2, $this->redis->llen('metavars'));
-
-        $this->assertEquals(0, $this->redis->llen('doesnotexist'));
-
-        // should throw an exception when trying to do a LLEN on non-list types
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->llen('foo');
-        });
-    }
-
-    function testListRange()
-    {
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers());
-
-        $this->assertEquals(
-            array_slice($numbers, 0, 4),
-            $this->redis->lrange('numbers', 0, 3)
-        );
-        $this->assertEquals(
-            array_slice($numbers, 4, 5),
-            $this->redis->lrange('numbers', 4, 8)
-        );
-        $this->assertEquals(
-            array_slice($numbers, 0, 1),
-            $this->redis->lrange('numbers', 0, 0)
-        );
-        $this->assertEquals(
-            array(),
-            $this->redis->lrange('numbers', 1, 0)
-        );
-        $this->assertEquals(
-            $numbers,
-            $this->redis->lrange('numbers', 0, -1)
-        );
-        $this->assertEquals(
-            array(5),
-            $this->redis->lrange('numbers', 5, -5)
-        );
-        $this->assertEquals(
-            array(),
-            $this->redis->lrange('numbers', 7, -5)
-        );
-        $this->assertEquals(
-            array_slice($numbers, -5, -1),
-            $this->redis->lrange('numbers', -5, -2)
-        );
-        $this->assertEquals(
-            $numbers,
-            $this->redis->lrange('numbers', -100, 100)
-        );
-
-        $this->assertEquals(
-            array(),
-            $this->redis->lrange('keyDoesNotExist', 0, 1)
-        );
-
-        // should throw an exception when trying to do a LRANGE on non-list types
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->lrange('foo', 0, -1);
-        });
-    }
-
-    function testListTrim()
-    {
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers());
-        $this->assertTrue($this->redis->ltrim('numbers', 0, 2));
-        $this->assertEquals(
-            array_slice($numbers, 0, 3),
-            $this->redis->lrange('numbers', 0, -1)
-        );
-
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers(), RC::WIPE_OUT);
-        $this->assertTrue($this->redis->ltrim('numbers', 5, 9));
-        $this->assertEquals(
-            array_slice($numbers, 5, 5),
-            $this->redis->lrange('numbers', 0, -1)
-        );
-
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers(), RC::WIPE_OUT);
-        $this->assertTrue($this->redis->ltrim('numbers', 0, -6));
-        $this->assertEquals(
-            array_slice($numbers, 0, -5),
-            $this->redis->lrange('numbers', 0, -1)
-        );
-
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers(), RC::WIPE_OUT);
-        $this->assertTrue($this->redis->ltrim('numbers', -5, -3));
-        $this->assertEquals(
-            array_slice($numbers, 5, 3),
-            $this->redis->lrange('numbers', 0, -1)
-        );
-
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers(), RC::WIPE_OUT);
-        $this->assertTrue($this->redis->ltrim('numbers', -100, 100));
-        $this->assertEquals(
-            $numbers,
-            $this->redis->lrange('numbers', 0, -1)
-        );
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->ltrim('foo', 0, 1);
-        });
-    }
-
-    function testListIndex()
-    {
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers());
-
-        $this->assertEquals(0, $this->redis->lindex('numbers', 0));
-        $this->assertEquals(5, $this->redis->lindex('numbers', 5));
-        $this->assertEquals(9, $this->redis->lindex('numbers', 9));
-        $this->assertNull($this->redis->lindex('numbers', 100));
-
-        $this->assertEquals(0, $this->redis->lindex('numbers', -0));
-        $this->assertEquals(9, $this->redis->lindex('numbers', -1));
-        $this->assertEquals(7, $this->redis->lindex('numbers', -3));
-        $this->assertNull($this->redis->lindex('numbers', -100));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->lindex('foo', 0);
-        });
-    }
-
-    function testListSet()
-    {
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers());
-
-        $this->assertTrue($this->redis->lset('numbers', 5, -5));
-        $this->assertEquals(-5, $this->redis->lindex('numbers', 5));
-
-        RC::testForServerException($this, RC::EXCEPTION_OUT_OF_RANGE, function($test) {
-            $test->redis->lset('numbers', 99, 99);
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->lset('foo', 0, 0);
-        });
-    }
-
-    function testListRemove()
-    {
-        $mixed = array(0, '_', 2, '_', 4, '_', 6, '_');
-
-        RC::pushTailAndReturn($this->redis, 'mixed', $mixed);
-        $this->assertEquals(2, $this->redis->lrem('mixed', 2, '_'));
-        $this->assertEquals(array(0, 2, 4, '_', 6, '_'), $this->redis->lrange('mixed', 0, -1));
-
-        RC::pushTailAndReturn($this->redis, 'mixed', $mixed, RC::WIPE_OUT);
-        $this->assertEquals(4, $this->redis->lrem('mixed', 0, '_'));
-        $this->assertEquals(array(0, 2, 4, 6), $this->redis->lrange('mixed', 0, -1));
-
-        RC::pushTailAndReturn($this->redis, 'mixed', $mixed, RC::WIPE_OUT);
-        $this->assertEquals(2, $this->redis->lrem('mixed', -2, '_'));
-        $this->assertEquals(array(0, '_', 2, '_', 4, 6), $this->redis->lrange('mixed', 0, -1));
-
-        RC::pushTailAndReturn($this->redis, 'mixed', $mixed, RC::WIPE_OUT);
-        $this->assertEquals(0, $this->redis->lrem('mixed', 2, '|'));
-        $this->assertEquals($mixed, $this->redis->lrange('mixed', 0, -1));
-
-        $this->assertEquals(0, $this->redis->lrem('listDoesNotExist', 2, '_'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->lrem('foo', 0, 0);
-        });
-    }
-
-    function testListPopFirst()
-    {
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', array(0, 1, 2, 3, 4));
-
-        $this->assertEquals(0, $this->redis->lpop('numbers'));
-        $this->assertEquals(1, $this->redis->lpop('numbers'));
-        $this->assertEquals(2, $this->redis->lpop('numbers'));
-
-        $this->assertEquals(array(3, 4), $this->redis->lrange('numbers', 0, -1));
-
-        $this->redis->lpop('numbers');
-        $this->redis->lpop('numbers');
-        $this->assertNull($this->redis->lpop('numbers'));
-
-        $this->assertNull($this->redis->lpop('numbers'));
-
-        $this->assertNull($this->redis->lpop('listDoesNotExist'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->lpop('foo');
-        });
-    }
-
-    function testListPopLast()
-    {
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', array(0, 1, 2, 3, 4));
-
-        $this->assertEquals(4, $this->redis->rpop('numbers'));
-        $this->assertEquals(3, $this->redis->rpop('numbers'));
-        $this->assertEquals(2, $this->redis->rpop('numbers'));
-
-        $this->assertEquals(array(0, 1), $this->redis->lrange('numbers', 0, -1));
-
-        $this->redis->rpop('numbers');
-        $this->redis->rpop('numbers');
-        $this->assertNull($this->redis->rpop('numbers'));
-
-        $this->assertNull($this->redis->rpop('numbers'));
-
-        $this->assertNull($this->redis->rpop('listDoesNotExist'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->rpop('foo');
-        });
-    }
-
-    function testListPopLastPushHead()
-    {
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', array(0, 1, 2));
-        $this->assertEquals(0, $this->redis->llen('temporary'));
-        $this->assertEquals(2, $this->redis->rpoplpush('numbers', 'temporary'));
-        $this->assertEquals(1, $this->redis->rpoplpush('numbers', 'temporary'));
-        $this->assertEquals(0, $this->redis->rpoplpush('numbers', 'temporary'));
-        $this->assertEquals(0, $this->redis->llen('numbers'));
-        $this->assertEquals(3, $this->redis->llen('temporary'));
-        $this->assertEquals(array(), $this->redis->lrange('numbers', 0, -1));
-        $this->assertEquals($numbers, $this->redis->lrange('temporary', 0, -1));
-
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', array(0, 1, 2));
-        $this->redis->rpoplpush('numbers', 'numbers');
-        $this->redis->rpoplpush('numbers', 'numbers');
-        $this->redis->rpoplpush('numbers', 'numbers');
-        $this->assertEquals($numbers, $this->redis->lrange('numbers', 0, -1));
-
-        $this->assertNull($this->redis->rpoplpush('listDoesNotExist1', 'listDoesNotExist2'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->rpoplpush('foo', 'hoge');
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->rpoplpush('temporary', 'foo');
-        });
-    }
-
-    function testListBlockingPopFirst()
-    {
-        // TODO: this test does not cover all the aspects of BLPOP/BRPOP as it
-        //       does not run with a concurrent client pushing items on lists.
-        RC::helperForBlockingPops('blpop');
-
-        // BLPOP on one key
-        $start = time();
-        $item = $this->redis->blpop('blpop3', 5);
-        $this->assertEquals((float)(time() - $start), 0, '', 1);
-        $this->assertEquals($item, array('blpop3', 'c'));
-
-        // BLPOP on more than one key
-        $poppedItems = array();
-        while ($item = $this->redis->blpop('blpop1', 'blpop2', 1)) {
-            $poppedItems[] = $item;
-        }
-        $this->assertEquals(
-            array(array('blpop1', 'a'), array('blpop1', 'd'), array('blpop2', 'b')),
-            $poppedItems
-        );
-
-        // check if BLPOP timeouts as expected on empty lists
-        $start = time();
-        $this->redis->blpop('blpop4', 2);
-        $this->assertEquals((float)(time() - $start), 2, '', 1);
-    }
-
-    function testListBlockingPopLast()
-    {
-        // TODO: this test does not cover all the aspects of BLPOP/BRPOP as it
-        //       does not run with a concurrent client pushing items on lists.
-        RC::helperForBlockingPops('brpop');
-
-        // BRPOP on one key
-        $start = time();
-        $item = $this->redis->brpop('brpop3', 5);
-        $this->assertEquals((float)(time() - $start), 0, '', 1);
-        $this->assertEquals($item, array('brpop3', 'c'));
-
-        // BRPOP on more than one key
-        $poppedItems = array();
-        while ($item = $this->redis->brpop('brpop1', 'brpop2', 1)) {
-            $poppedItems[] = $item;
-        }
-        $this->assertEquals(
-            array(array('brpop1', 'd'), array('brpop1', 'a'), array('brpop2', 'b')),
-            $poppedItems
-        );
-
-        // check if BRPOP timeouts as expected on empty lists
-        $start = time();
-        $this->redis->brpop('brpop4', 2);
-        $this->assertEquals((float)(time() - $start), 2, '', 1);
-    }
-
-    function testListBlockingPopLastPushHead()
-    {
-        // TODO: this test does not cover all the aspects of BLPOP/BRPOP as it
-        //       does not run with a concurrent client pushing items on lists.
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', array(1, 2, 3));
-        $src_count = count($numbers);
-        $dst_count = 0;
-
-        while ($item = $this->redis->brpoplpush('numbers', 'temporary', 1)) {
-            $this->assertEquals(--$src_count, $this->redis->llen('numbers'));
-            $this->assertEquals(++$dst_count, $this->redis->llen('temporary'));
-            $this->assertEquals(array_pop($numbers), $this->redis->lindex('temporary', 0));
-        }
-
-        $start = time();
-        $this->assertNull($this->redis->brpoplpush('numbers', 'temporary', 2));
-        $this->assertEquals(2, (float)(time() - $start), '', 1);
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->del('numbers');
-            $test->redis->del('temporary');
-            $test->redis->set('numbers', 'foobar');
-            $test->redis->brpoplpush('numbers', 'temporary', 1);
-        });
-    }
-
-    function testListInsert()
-    {
-        $numbers = RC::pushTailAndReturn($this->redis, 'numbers', RC::getArrayOfNumbers());
-
-        $this->assertEquals(11, $this->redis->linsert('numbers', 'before', 0, -2));
-        $this->assertEquals(12, $this->redis->linsert('numbers', 'after', -2, -1));
-        $this->assertEquals(array(-2, -1, 0, 1), $this->redis->lrange('numbers', 0, 3));
-
-        $this->assertEquals(-1, $this->redis->linsert('numbers', 'after', 100, 200));
-        $this->assertEquals(-1, $this->redis->linsert('numbers', 'before', 100, 50));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->linsert('foo', 'before', 0, 0);
-        });
-    }
-
-    /* commands operating on sets */
-
-    function testSetAdd()
-    {
-        $this->assertTrue($this->redis->sadd('set', 0));
-        $this->assertTrue($this->redis->sadd('set', 1));
-        $this->assertFalse($this->redis->sadd('set', 0));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->sadd('foo', 0);
-        });
-    }
-
-    function testSetRemove()
-    {
-        $set = RC::setAddAndReturn($this->redis, 'set', array(0, 1, 2, 3, 4));
-
-        $this->assertTrue($this->redis->srem('set', 0));
-        $this->assertTrue($this->redis->srem('set', 4));
-        $this->assertFalse($this->redis->srem('set', 10));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->srem('foo', 0);
-        });
-    }
-
-    function testSetPop()
-    {
-        $set = RC::setAddAndReturn($this->redis, 'set', array(0, 1, 2, 3, 4));
-
-        $this->assertTrue(in_array($this->redis->spop('set'), $set));
-
-        $this->assertNull($this->redis->spop('setDoesNotExist'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->spop('foo');
-        });
-    }
-
-    function testSetMove()
-    {
-        $setA = RC::setAddAndReturn($this->redis, 'setA', array(0, 1, 2, 3, 4, 5));
-        $setB = RC::setAddAndReturn($this->redis, 'setB', array(5, 6, 7, 8, 9, 10));
-
-        $this->assertTrue($this->redis->smove('setA', 'setB', 0));
-        $this->assertFalse($this->redis->srem('setA', 0));
-        $this->assertTrue($this->redis->srem('setB', 0));
-
-        $this->assertTrue($this->redis->smove('setA', 'setB', 5));
-        $this->assertFalse($this->redis->smove('setA', 'setB', 100));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->smove('foo', 'setB', 5);
-        });
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->smove('setA', 'foo', 5);
-        });
-    }
-
-    function testSetCardinality()
-    {
-        RC::setAddAndReturn($this->redis, 'setA', array(0, 1, 2, 3, 4, 5));
-
-        $this->assertEquals(6, $this->redis->scard('setA'));
-
-        // empty set
-        $this->redis->sadd('setB', 0);
-        $this->redis->spop('setB');
-        $this->assertEquals(0, $this->redis->scard('setB'));
-
-        // non-existing set
-        $this->assertEquals(0, $this->redis->scard('setDoesNotExist'));
-
-        // wrong type
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->scard('foo');
-        });
-    }
-
-    function testSetIsMember()
-    {
-        RC::setAddAndReturn($this->redis, 'set', array(0, 1, 2, 3, 4, 5));
-
-        $this->assertTrue($this->redis->sismember('set', 3));
-        $this->assertFalse($this->redis->sismember('set', 100));
-
-        $this->assertFalse($this->redis->sismember('setDoesNotExist', 0));
-
-        // wrong type
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->sismember('foo', 0);
-        });
-    }
-
-    function testSetMembers()
-    {
-        $set = RC::setAddAndReturn($this->redis, 'set', array(0, 1, 2, 3, 4, 5, 6));
-
-        $this->assertTrue(RC::sameValuesInArrays($set, $this->redis->smembers('set')));
-
-        $this->assertEquals(array(), $this->redis->smembers('setDoesNotExist'));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->smembers('foo');
-        });
-    }
-
-    function testSetIntersection()
-    {
-        $setA = RC::setAddAndReturn($this->redis, 'setA', array(0, 1, 2, 3, 4, 5, 6));
-        $setB = RC::setAddAndReturn($this->redis, 'setB', array(1, 3, 4, 6, 9, 10));
-
-        $this->assertTrue(RC::sameValuesInArrays(
-            $setA,
-            $this->redis->sinter('setA')
-        ));
-
-        $this->assertTrue(RC::sameValuesInArrays(
-            array_intersect($setA, $setB),
-            $this->redis->sinter('setA', 'setB')
-        ));
-
-        $this->assertEquals(array(), $this->redis->sinter('setA', 'setDoesNotExist'));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->sinter('foo');
-        });
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->sinter('setA', 'foo');
-        });
-    }
-
-    function testSetIntersectionStore()
-    {
-        $setA = RC::setAddAndReturn($this->redis, 'setA', array(0, 1, 2, 3, 4, 5, 6));
-        $setB = RC::setAddAndReturn($this->redis, 'setB', array(1, 3, 4, 6, 9, 10));
-
-        $this->assertEquals(count($setA), $this->redis->sinterstore('setC', 'setA'));
-        $this->assertTrue(RC::sameValuesInArrays(
-            $setA,
-            $this->redis->smembers('setC')
-        ));
-
-        $this->redis->del('setC');
-        $this->assertEquals(4, $this->redis->sinterstore('setC', 'setA', 'setB'));
-        $this->assertTrue(RC::sameValuesInArrays(
-            array(1, 3, 4, 6),
-            $this->redis->smembers('setC')
-        ));
-
-        $this->redis->del('setC');
-        $this->assertEquals(array(), $this->redis->sinter('setC', 'setDoesNotExist'));
-        $this->assertFalse($this->redis->exists('setC'));
-
-        // existing keys are replaced by SINTERSTORE
-        $this->redis->set('foo', 'bar');
-        $this->assertEquals(count($setA), $this->redis->sinterstore('foo', 'setA'));
-
-        // accepts an array for the list of source keys
-        $this->assertEquals(4, $this->redis->sinterstore('setC', array('setA', 'setB')));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->sinterstore('setA', 'foo');
-        });
-    }
-
-    function testSetUnion()
-    {
-        $setA = RC::setAddAndReturn($this->redis, 'setA', array(0, 1, 2, 3, 4, 5, 6));
-        $setB = RC::setAddAndReturn($this->redis, 'setB', array(1, 3, 4, 6, 9, 10));
-
-        $this->assertTrue(RC::sameValuesInArrays(
-            $setA,
-            $this->redis->sunion('setA')
-        ));
-
-        $this->assertTrue(RC::sameValuesInArrays(
-            array_union($setA, $setB),
-            $this->redis->sunion('setA', 'setB')
-        ));
-
-        $this->assertTrue(RC::sameValuesInArrays(
-            $setA,
-            $this->redis->sunion('setA', 'setDoesNotExist')
-        ));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->sunion('foo');
-        });
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->sunion('setA', 'foo');
-        });
-    }
-
-    function testSetUnionStore()
-    {
-        $setA = RC::setAddAndReturn($this->redis, 'setA', array(0, 1, 2, 3, 4, 5, 6));
-        $setB = RC::setAddAndReturn($this->redis, 'setB', array(1, 3, 4, 6, 9, 10));
-
-        $this->assertEquals(count($setA), $this->redis->sunionstore('setC', 'setA'));
-        $this->assertTrue(RC::sameValuesInArrays(
-            $setA,
-            $this->redis->smembers('setC')
-        ));
-
-        $this->redis->del('setC');
-        $this->assertEquals(9, $this->redis->sunionstore('setC', 'setA', 'setB'));
-        $this->assertTrue(RC::sameValuesInArrays(
-            array_union($setA, $setB),
-            $this->redis->smembers('setC')
-        ));
-
-        // non-existing keys are considered empty sets
-        $this->redis->del('setC');
-        $this->assertEquals(0, $this->redis->sunionstore('setC', 'setDoesNotExist'));
-        $this->assertFalse($this->redis->exists('setC'));
-        $this->assertEquals(0, $this->redis->scard('setC'));
-
-        // existing keys are replaced by SUNIONSTORE
-        $this->redis->set('foo', 'bar');
-        $this->assertEquals(count($setA), $this->redis->sunionstore('foo', 'setA'));
-
-        // accepts an array for the list of source keys
-        $this->assertEquals(9, $this->redis->sunionstore('setC', array('setA', 'setB')));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->sunionstore('setA', 'foo');
-        });
-    }
-
-    function testSetDifference()
-    {
-        $setA = RC::setAddAndReturn($this->redis, 'setA', array(0, 1, 2, 3, 4, 5, 6));
-        $setB = RC::setAddAndReturn($this->redis, 'setB', array(1, 3, 4, 6, 9, 10));
-
-        $this->assertTrue(RC::sameValuesInArrays(
-            $setA,
-            $this->redis->sdiff('setA')
-        ));
-
-        $this->assertTrue(RC::sameValuesInArrays(
-            array_diff($setA, $setB),
-            $this->redis->sdiff('setA', 'setB')
-        ));
-
-        $this->assertTrue(RC::sameValuesInArrays(
-            $setA,
-            $this->redis->sdiff('setA', 'setDoesNotExist')
-        ));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->sdiff('foo');
-        });
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->sdiff('setA', 'foo');
-        });
-    }
-
-    function testSetDifferenceStore()
-    {
-        $setA = RC::setAddAndReturn($this->redis, 'setA', array(0, 1, 2, 3, 4, 5, 6));
-        $setB = RC::setAddAndReturn($this->redis, 'setB', array(1, 3, 4, 6, 9, 10));
-
-        $this->assertEquals(count($setA), $this->redis->sdiffstore('setC', 'setA'));
-        $this->assertTrue(RC::sameValuesInArrays(
-            $setA,
-            $this->redis->smembers('setC')
-        ));
-
-        $this->redis->del('setC');
-        $this->assertEquals(3, $this->redis->sdiffstore('setC', 'setA', 'setB'));
-        $this->assertTrue(RC::sameValuesInArrays(
-            array_diff($setA, $setB),
-            $this->redis->smembers('setC')
-        ));
-
-        // non-existing keys are considered empty sets
-        $this->redis->del('setC');
-        $this->assertEquals(0, $this->redis->sdiffstore('setC', 'setDoesNotExist'));
-        $this->assertFalse($this->redis->exists('setC'));
-        $this->assertEquals(0, $this->redis->scard('setC'));
-
-        // existing keys are replaced by SDIFFSTORE
-        $this->redis->set('foo', 'bar');
-        $this->assertEquals(count($setA), $this->redis->sdiffstore('foo', 'setA'));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->sdiffstore('setA', 'foo');
-        });
-    }
-
-    function testRandomMember()
-    {
-        $set = RC::setAddAndReturn($this->redis, 'set', array(0, 1, 2, 3, 4, 5, 6));
-
-        $this->assertTrue(in_array($this->redis->srandmember('set'), $set));
-
-        $this->assertNull($this->redis->srandmember('setDoesNotExist'));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->srandmember('foo');
-        });
-    }
-
-    /* commands operating on sorted sets */
-
-    function testZsetAdd()
-    {
-        $this->assertTrue($this->redis->zadd('zset', 0, 'a'));
-        $this->assertTrue($this->redis->zadd('zset', 1, 'b'));
-
-        $this->assertTrue($this->redis->zadd('zset', -1, 'c'));
-
-        // TODO: floats?
-        //$this->assertTrue($this->redis->zadd('zset', -1.0, 'c'));
-        //$this->assertTrue($this->redis->zadd('zset', -1.0, 'c'));
-
-        $this->assertFalse($this->redis->zadd('zset', 2, 'b'));
-        $this->assertFalse($this->redis->zadd('zset', -2, 'b'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zadd('foo', 0, 'a');
-        });
-    }
-
-    function testZsetIncrementBy()
-    {
-        $this->assertEquals(1, $this->redis->zincrby('zsetDoesNotExist', 1, 'foo'));
-        $this->assertEquals('zset', $this->redis->type('zsetDoesNotExist'));
-
-        RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-        $this->assertEquals(-5, $this->redis->zincrby('zset', 5, 'a'));
-        $this->assertEquals(1, $this->redis->zincrby('zset', 1, 'b'));
-        $this->assertEquals(10, $this->redis->zincrby('zset', 0, 'c'));
-        $this->assertEquals(0, $this->redis->zincrby('zset', -20, 'd'));
-        $this->assertEquals(2, $this->redis->zincrby('zset', 2, 'd'));
-        $this->assertEquals(-10, $this->redis->zincrby('zset', -30, 'e'));
-        $this->assertEquals(1, $this->redis->zincrby('zset', 1, 'x'));
-
-        // wrong type
-        $this->redis->set('foo', 'bar');
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->zincrby('foo', 1, 'a');
-        });
-    }
-
-    function testZsetRemove()
-    {
-        RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertTrue($this->redis->zrem('zset', 'a'));
-        $this->assertFalse($this->redis->zrem('zset', 'x'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zrem('foo', 'bar');
-        });
-    }
-
-    function testZsetRange()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(
-            array_slice(array_keys($zset), 0, 4),
-            $this->redis->zrange('zset', 0, 3)
-        );
-
-        $this->assertEquals(
-            array_slice(array_keys($zset), 0, 1),
-            $this->redis->zrange('zset', 0, 0)
-        );
-
-        $this->assertEquals(
-            array(),
-            $this->redis->zrange('zset', 1, 0)
-        );
-
-        $this->assertEquals(
-            array_values(array_keys($zset)),
-            $this->redis->zrange('zset', 0, -1)
-        );
-
-        $this->assertEquals(
-            array_slice(array_keys($zset), 3, 1),
-            $this->redis->zrange('zset', 3, -3)
-        );
-
-        $this->assertEquals(
-            array(),
-            $this->redis->zrange('zset', 5, -3)
-        );
-
-        $this->assertEquals(
-            array_slice(array_keys($zset), -5, -1),
-            $this->redis->zrange('zset', -5, -2)
-        );
-
-        $this->assertEquals(
-            array_values(array_keys($zset)),
-            $this->redis->zrange('zset', -100, 100)
-        );
-
-        $this->assertEquals(
-            array_values(array_keys($zset)),
-            $this->redis->zrange('zset', -100, 100)
-        );
-
-        $this->assertEquals(
-            array(array('a', -10), array('b', 0), array('c', 10)),
-            $this->redis->zrange('zset', 0, 2, 'withscores')
-        );
-
-        $this->assertEquals(
-            array(array('a', -10), array('b', 0), array('c', 10)),
-            $this->redis->zrange('zset', 0, 2, array('withscores' => true))
-        );
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zrange('foo', 0, -1);
-        });
-    }
-
-    function testZsetReverseRange()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(
-            array_slice(array_reverse(array_keys($zset)), 0, 4),
-            $this->redis->zrevrange('zset', 0, 3)
-        );
-
-        $this->assertEquals(
-            array_slice(array_reverse(array_keys($zset)), 0, 1),
-            $this->redis->zrevrange('zset', 0, 0)
-        );
-
-        $this->assertEquals(
-            array(),
-            $this->redis->zrevrange('zset', 1, 0)
-        );
-
-        $this->assertEquals(
-            array_values(array_reverse(array_keys($zset))),
-            $this->redis->zrevrange('zset', 0, -1)
-        );
-
-        $this->assertEquals(
-            array_slice(array_reverse(array_keys($zset)), 3, 1),
-            $this->redis->zrevrange('zset', 3, -3)
-        );
-
-        $this->assertEquals(
-            array(),
-            $this->redis->zrevrange('zset', 5, -3)
-        );
-
-        $this->assertEquals(
-            array_slice(array_reverse(array_keys($zset)), -5, -1),
-            $this->redis->zrevrange('zset', -5, -2)
-        );
-
-        $this->assertEquals(
-            array_values(array_reverse(array_keys($zset))),
-            $this->redis->zrevrange('zset', -100, 100)
-        );
-
-        $this->assertEquals(
-            array(array('f', 30), array('e', 20), array('d', 20)),
-            $this->redis->zrevrange('zset', 0, 2, 'withscores')
-        );
-
-        $this->assertEquals(
-            array(array('f', 30), array('e', 20), array('d', 20)),
-            $this->redis->zrevrange('zset', 0, 2, array('withscores' => true))
-        );
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zrevrange('foo', 0, -1);
-        });
-    }
-
-    function testZsetRangeByScore()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(
-            array('a'),
-            $this->redis->zrangebyscore('zset', -10, -10)
-        );
-
-        $this->assertEquals(
-            array('c', 'd', 'e', 'f'),
-            $this->redis->zrangebyscore('zset', 10, 30)
-        );
-
-        $this->assertEquals(
-            array('d', 'e'),
-            $this->redis->zrangebyscore('zset', 20, 20)
-        );
-
-        $this->assertEquals(
-            array(),
-            $this->redis->zrangebyscore('zset', 30, 0)
-        );
-
-        $this->assertEquals(
-            array(array('c', 10), array('d', 20), array('e', 20)),
-            $this->redis->zrangebyscore('zset', 10, 20, 'withscores')
-        );
-
-        $this->assertEquals(
-            array(array('c', 10), array('d', 20), array('e', 20)),
-            $this->redis->zrangebyscore('zset', 10, 20, array('withscores' => true))
-        );
-
-        $this->assertEquals(
-            array('d', 'e'),
-            $this->redis->zrangebyscore('zset', 10, 20, array('limit' => array(1, 2)))
-        );
-
-        $this->assertEquals(
-            array('d', 'e'),
-            $this->redis->zrangebyscore('zset', 10, 20, array(
-                'limit' => array('offset' => 1, 'count' => 2)
-            ))
-        );
-
-        $this->assertEquals(
-            array(array('d', 20), array('e', 20)),
-            $this->redis->zrangebyscore('zset', 10, 20, array(
-                'limit'      => array(1, 2),
-                'withscores' => true,
-            ))
-        );
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zrangebyscore('foo', 0, 0);
-        });
-    }
-
-    function testZsetReverseRangeByScore()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(
-            array('a'),
-            $this->redis->zrevrangebyscore('zset', -10, -10)
-        );
-
-        $this->assertEquals(
-            array('b', 'a'),
-            $this->redis->zrevrangebyscore('zset', 0, -10)
-        );
-
-        $this->assertEquals(
-            array('e', 'd'),
-            $this->redis->zrevrangebyscore('zset', 20, 20)
-        );
-
-        $this->assertEquals(
-            array('f', 'e', 'd', 'c', 'b'),
-            $this->redis->zrevrangebyscore('zset', 30, 0)
-        );
-
-        $this->assertEquals(
-            array(array('e', 20), array('d', 20), array('c', 10)),
-            $this->redis->zrevrangebyscore('zset', 20, 10, 'withscores')
-        );
-
-        $this->assertEquals(
-            array(array('e', 20), array('d', 20), array('c', 10)),
-            $this->redis->zrevrangebyscore('zset', 20, 10, array('withscores' => true))
-        );
-
-        $this->assertEquals(
-            array('d', 'c'),
-            $this->redis->zrevrangebyscore('zset', 20, 10, array('limit' => array(1, 2)))
-        );
-
-        $this->assertEquals(
-            array('d', 'c'),
-            $this->redis->zrevrangebyscore('zset', 20, 10, array(
-                'limit' => array('offset' => 1, 'count' => 2)
-            ))
-        );
-
-        $this->assertEquals(
-            array(array('d', 20), array('c', 10)),
-            $this->redis->zrevrangebyscore('zset', 20, 10, array(
-                'limit'      => array(1, 2),
-                'withscores' => true,
-            ))
-        );
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zrevrangebyscore('foo', 0, 0);
-        });
-    }
-
-    function testZsetUnionStore()
-    {
-        $zsetA = RC::zsetAddAndReturn($this->redis, 'zseta', array('a' => 1, 'b' => 2, 'c' => 3));
-        $zsetB = RC::zsetAddAndReturn($this->redis, 'zsetb', array('b' => 1, 'c' => 2, 'd' => 3));
-
-        // basic ZUNIONSTORE
-        $this->assertEquals(4, $this->redis->zunionstore('zsetc', 2, 'zseta', 'zsetb'));
-        $this->assertEquals(
-            array(array('a', 1), array('b', 3), array('d', 3), array('c', 5)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        $this->assertEquals(3, $this->redis->zunionstore('zsetc', 2, 'zseta', 'zsetbNull'));
-        $this->assertEquals(
-            array(array('a', 1), array('b', 2), array('c', 3)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        $this->assertEquals(3, $this->redis->zunionstore('zsetc', 2, 'zsetaNull', 'zsetb'));
-        $this->assertEquals(
-            array(array('b', 1), array('c', 2), array('d', 3)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        $this->assertEquals(0, $this->redis->zunionstore('zsetc', 2, 'zsetaNull', 'zsetbNull'));
-
-        // with WEIGHTS
-        $options = array('weights' => array(2, 3));
-        $this->assertEquals(4, $this->redis->zunionstore('zsetc', 2, 'zseta', 'zsetb', $options));
-        $this->assertEquals(
-            array(array('a', 2), array('b', 7), array('d', 9), array('c', 12)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        // with AGGREGATE (min)
-        $options = array('aggregate' => 'min');
-        $this->assertEquals(4, $this->redis->zunionstore('zsetc', 2, 'zseta', 'zsetb', $options));
-        $this->assertEquals(
-            array(array('a', 1), array('b', 1), array('c', 2), array('d', 3)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        // with AGGREGATE (max)
-        $options = array('aggregate' => 'max');
-        $this->assertEquals(4, $this->redis->zunionstore('zsetc', 2, 'zseta', 'zsetb', $options));
-        $this->assertEquals(
-            array(array('a', 1), array('b', 2), array('c', 3), array('d', 3)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        // using an array to pass the list of source keys
-        $sourceKeys = array('zseta', 'zsetb');
-
-        $this->assertEquals(4, $this->redis->zunionstore('zsetc', $sourceKeys));
-        $this->assertEquals(
-            array(array('a', 1), array('b', 3), array('d', 3), array('c', 5)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        // using an array to pass the list of source keys + options array
-        $options = array('weights' => array(2, 3));
-        $this->assertEquals(4, $this->redis->zunionstore('zsetc', $sourceKeys, $options));
-        $this->assertEquals(
-            array(array('a', 2), array('b', 7), array('d', 9), array('c', 12)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('zsetFake', 'fake');
-            $test->redis->zunionstore('zsetc', 2, 'zseta', 'zsetFake');
-        });
-    }
-
-    function testZsetIntersectionStore()
-    {
-        $zsetA = RC::zsetAddAndReturn($this->redis, 'zseta', array('a' => 1, 'b' => 2, 'c' => 3));
-        $zsetB = RC::zsetAddAndReturn($this->redis, 'zsetb', array('b' => 1, 'c' => 2, 'd' => 3));
-
-        // basic ZINTERSTORE
-        $this->assertEquals(2, $this->redis->zinterstore('zsetc', 2, 'zseta', 'zsetb'));
-        $this->assertEquals(
-            array(array('b', 3), array('c', 5)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        $this->assertEquals(0, $this->redis->zinterstore('zsetc', 2, 'zseta', 'zsetbNull'));
-        $this->assertEquals(0, $this->redis->zinterstore('zsetc', 2, 'zsetaNull', 'zsetb'));
-        $this->assertEquals(0, $this->redis->zinterstore('zsetc', 2, 'zsetaNull', 'zsetbNull'));
-
-        // with WEIGHTS
-        $options = array('weights' => array(2, 3));
-        $this->assertEquals(2, $this->redis->zinterstore('zsetc', 2, 'zseta', 'zsetb', $options));
-        $this->assertEquals(
-            array(array('b', 7), array('c', 12)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        // with AGGREGATE (min)
-        $options = array('aggregate' => 'min');
-        $this->assertEquals(2, $this->redis->zinterstore('zsetc', 2, 'zseta', 'zsetb', $options));
-        $this->assertEquals(
-            array(array('b', 1), array('c', 2)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        // with AGGREGATE (max)
-        $options = array('aggregate' => 'max');
-        $this->assertEquals(2, $this->redis->zinterstore('zsetc', 2, 'zseta', 'zsetb', $options));
-        $this->assertEquals(
-            array(array('b', 2), array('c', 3)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        // using an array to pass the list of source keys
-        $sourceKeys = array('zseta', 'zsetb');
-
-        $this->assertEquals(2, $this->redis->zinterstore('zsetc', $sourceKeys));
-        $this->assertEquals(
-            array(array('b', 3), array('c', 5)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        // using an array to pass the list of source keys + options array
-        $options = array('weights' => array(2, 3));
-        $this->assertEquals(2, $this->redis->zinterstore('zsetc', $sourceKeys, $options));
-        $this->assertEquals(
-            array(array('b', 7), array('c', 12)),
-            $this->redis->zrange('zsetc', 0, -1, 'withscores')
-        );
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('zsetFake', 'fake');
-            $test->redis->zinterstore('zsetc', 2, 'zseta', 'zsetFake');
-        });
-    }
-
-    function testZsetCount()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(0, $this->redis->zcount('zset', 50, 100));
-        $this->assertEquals(6, $this->redis->zcount('zset', -100, 100));
-        $this->assertEquals(3, $this->redis->zcount('zset', 10, 20));
-        $this->assertEquals(2, $this->redis->zcount('zset', "(10", 20));
-        $this->assertEquals(1, $this->redis->zcount('zset', 10, "(20"));
-        $this->assertEquals(0, $this->redis->zcount('zset', "(10", "(20"));
-        $this->assertEquals(3, $this->redis->zcount('zset', "(0", "(30"));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zcount('foo', 0, 0);
-        });
-    }
-
-    function testZsetCardinality()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(count($zset), $this->redis->zcard('zset'));
-
-        $this->redis->zrem('zset', 'a');
-        $this->assertEquals(count($zset) - 1, $this->redis->zcard('zset'));
-
-        // empty zset
-        $this->redis->zadd('zsetB', 0, 'a');
-        $this->redis->zrem('zsetB', 'a');
-        $this->assertEquals(0, $this->redis->zcard('setB'));
-
-        // non-existing zset
-        $this->assertEquals(0, $this->redis->zcard('zsetDoesNotExist'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zcard('foo');
-        });
-    }
-
-    function testZsetScore()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(-10, $this->redis->zscore('zset', 'a'));
-        $this->assertEquals(10, $this->redis->zscore('zset', 'c'));
-        $this->assertEquals(20, $this->redis->zscore('zset', 'e'));
-
-        $this->assertNull($this->redis->zscore('zset', 'x'));
-        $this->assertNull($this->redis->zscore('zsetDoesNotExist', 'a'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zscore('foo', 'bar');
-        });
-    }
-
-    function testZsetRemoveRangeByScore()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(2, $this->redis->zremrangebyscore('zset', -10, 0));
-        $this->assertEquals(
-            array('c', 'd', 'e', 'f'),
-            $this->redis->zrange('zset', 0, -1)
-        );
-
-        $this->assertEquals(1, $this->redis->zremrangebyscore('zset', 10, 10));
-        $this->assertEquals(
-            array('d', 'e', 'f'),
-            $this->redis->zrange('zset', 0, -1)
-        );
-
-        $this->assertEquals(0, $this->redis->zremrangebyscore('zset', 100, 100));
-
-        $this->assertEquals(3, $this->redis->zremrangebyscore('zset', 0, 100));
-        $this->assertEquals(0, $this->redis->zremrangebyscore('zset', 0, 100));
-
-        $this->assertEquals(0, $this->redis->zremrangebyscore('zsetDoesNotExist', 0, 100));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zremrangebyscore('foo', 0, 0);
-        });
-    }
-
-    function testZsetRank()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(0, $this->redis->zrank('zset', 'a'));
-        $this->assertEquals(1, $this->redis->zrank('zset', 'b'));
-        $this->assertEquals(4, $this->redis->zrank('zset', 'e'));
-
-        $this->redis->zrem('zset', 'd');
-        $this->assertEquals(3, $this->redis->zrank('zset', 'e'));
-
-        $this->assertNull($this->redis->zrank('zset', 'x'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zrank('foo', 'a');
-        });
-    }
-
-    function testZsetReverseRank()
-    {
-        $zset = RC::zsetAddAndReturn($this->redis, 'zset', RC::getZSetArray());
-
-        $this->assertEquals(5, $this->redis->zrevrank('zset', 'a'));
-        $this->assertEquals(4, $this->redis->zrevrank('zset', 'b'));
-        $this->assertEquals(1, $this->redis->zrevrank('zset', 'e'));
-
-        $this->redis->zrem('zset', 'e');
-        $this->assertEquals(1, $this->redis->zrevrank('zset', 'd'));
-
-        $this->assertNull($this->redis->zrevrank('zset', 'x'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zrevrank('foo', 'a');
-        });
-    }
-
-    function testZsetRemoveRangeByRank()
-    {
-        RC::zsetAddAndReturn($this->redis, 'zseta', RC::getZSetArray());
-
-        $this->assertEquals(3, $this->redis->zremrangebyrank('zseta', 0, 2));
-        $this->assertEquals(
-            array('d', 'e', 'f'),
-            $this->redis->zrange('zseta', 0, -1)
-        );
-
-        $this->assertEquals(1, $this->redis->zremrangebyrank('zseta', 0, 0));
-        $this->assertEquals(
-            array('e', 'f'),
-            $this->redis->zrange('zseta', 0, -1)
-        );
-
-        RC::zsetAddAndReturn($this->redis, 'zsetb', RC::getZSetArray());
-        $this->assertEquals(3, $this->redis->zremrangebyrank('zsetb', -3, -1));
-        $this->assertEquals(
-            array('a', 'b', 'c'),
-            $this->redis->zrange('zsetb', 0, -1)
-        );
-
-        $this->assertEquals(1, $this->redis->zremrangebyrank('zsetb', -1, -1));
-        $this->assertEquals(
-            array('a', 'b'),
-            $this->redis->zrange('zsetb', 0, -1)
-        );
-
-        $this->assertEquals(2, $this->redis->zremrangebyrank('zsetb', -2, 1));
-        $this->assertEquals(
-            array(),
-            $this->redis->zrange('zsetb', 0, -1)
-        );
-        $this->assertFalse($this->redis->exists('zsetb'));
-
-        $this->assertEquals(0, $this->redis->zremrangebyrank('zsetc', 0, 0));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->zremrangebyrank('foo', 0, 1);
-        });
-    }
-
-    /* commands operating on hashes */
-
-    function testHashSet()
-    {
-        $this->assertTrue($this->redis->hset('metavars', 'foo', 'bar'));
-        $this->assertTrue($this->redis->hset('metavars', 'hoge', 'piyo'));
-        $this->assertEquals('bar', $this->redis->hget('metavars', 'foo'));
-        $this->assertEquals('piyo', $this->redis->hget('metavars', 'hoge'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('test', 'foobar');
-            $test->redis->hset('test', 'hoge', 'piyo');
-        });
-    }
-
-    function testHashGet()
-    {
-        $this->assertTrue($this->redis->hset('metavars', 'foo', 'bar'));
-        $this->assertEquals('bar', $this->redis->hget('metavars', 'foo'));
-
-        $this->assertEquals('bar', $this->redis->hget('metavars', 'foo'));
-        $this->assertNull($this->redis->hget('metavars', 'hoge'));
-        $this->assertNull($this->redis->hget('hashDoesNotExist', 'field'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->rpush('metavars', 'foo');
-            $test->redis->hget('metavars', 'foo');
-        });
-    }
-
-    function testHashExists()
-    {
-        $this->assertTrue($this->redis->hset('metavars', 'foo', 'bar'));
-        $this->assertTrue($this->redis->hexists('metavars', 'foo'));
-        $this->assertFalse($this->redis->hexists('metavars', 'hoge'));
-        $this->assertFalse($this->redis->hexists('hashDoesNotExist', 'field'));
-    }
-
-    function testHashDelete()
-    {
-        $this->assertTrue($this->redis->hset('metavars', 'foo', 'bar'));
-        $this->assertTrue($this->redis->hexists('metavars', 'foo'));
-        $this->assertTrue($this->redis->hdel('metavars', 'foo'));
-        $this->assertFalse($this->redis->hexists('metavars', 'foo'));
-
-        $this->assertFalse($this->redis->hdel('metavars', 'hoge'));
-        $this->assertFalse($this->redis->hdel('hashDoesNotExist', 'field'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->hdel('foo', 'field');
-        });
-    }
-
-    function testHashLength()
-    {
-        $this->assertTrue($this->redis->hset('metavars', 'foo', 'bar'));
-        $this->assertTrue($this->redis->hset('metavars', 'hoge', 'piyo'));
-        $this->assertTrue($this->redis->hset('metavars', 'foofoo', 'barbar'));
-        $this->assertTrue($this->redis->hset('metavars', 'hogehoge', 'piyopiyo'));
-
-        $this->assertEquals(4, $this->redis->hlen('metavars'));
-        $this->assertEquals(0, $this->redis->hlen('hashDoesNotExist'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->hlen('foo');
-        });
-    }
-
-    function testHashSetPreserve()
-    {
-        $this->assertTrue($this->redis->hsetnx('metavars', 'foo', 'bar'));
-        $this->assertFalse($this->redis->hsetnx('metavars', 'foo', 'barbar'));
-        $this->assertEquals('bar', $this->redis->hget('metavars', 'foo'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('test', 'foobar');
-            $test->redis->hsetnx('test', 'hoge', 'piyo');
-        });
-    }
-
-    function testHashSetAndGetMultiple()
-    {
-        $hashKVs = array('foo' => 'bar', 'hoge' => 'piyo');
-
-        // key=>value pairs via array instance
-        $this->assertTrue($this->redis->hmset('metavars', $hashKVs));
-        $multiRet = $this->redis->hmget('metavars', array_keys($hashKVs));
-        $this->assertEquals($hashKVs, array_combine(array_keys($hashKVs), array_values($multiRet)));
-
-        // key=>value pairs via function arguments
-        $this->redis->del('metavars');
-        $this->assertTrue($this->redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo'));
-        $this->assertEquals(array('bar', 'piyo'), $this->redis->hmget('metavars', 'foo', 'hoge'));
-    }
-
-    function testHashIncrementBy()
-    {
-        // test subsequent increment commands
-        $this->assertEquals(10, $this->redis->hincrby('hash', 'counter', 10));
-        $this->assertEquals(20, $this->redis->hincrby('hash', 'counter', 10));
-        $this->assertEquals(0, $this->redis->hincrby('hash', 'counter', -20));
-
-        RC::testForServerException($this, RC::EXCEPTION_HASH_VALNOTINT, function($test) {
-            $test->redis->hset('hash', 'field', 'stringvalue');
-            $test->redis->hincrby('hash', 'field', 10);
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->hincrby('foo', 'bar', 1);
-        });
-    }
-
-    function testHashKeys()
-    {
-        $hashKVs = array('foo' => 'bar', 'hoge' => 'piyo');
-        $this->assertTrue($this->redis->hmset('metavars', $hashKVs));
-
-        $this->assertEquals(array_keys($hashKVs), $this->redis->hkeys('metavars'));
-        $this->assertEquals(array(), $this->redis->hkeys('hashDoesNotExist'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->hkeys('foo');
-        });
-    }
-
-    function testHashValues()
-    {
-        $hashKVs = array('foo' => 'bar', 'hoge' => 'piyo');
-        $this->assertTrue($this->redis->hmset('metavars', $hashKVs));
-
-        $this->assertEquals(array_values($hashKVs), $this->redis->hvals('metavars'));
-        $this->assertEquals(array(), $this->redis->hvals('hashDoesNotExist'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->hvals('foo');
-        });
-    }
-
-    function testHashGetAll()
-    {
-        $hashKVs = array('foo' => 'bar', 'hoge' => 'piyo');
-        $this->assertTrue($this->redis->hmset('metavars', $hashKVs));
-
-        $this->assertEquals($hashKVs, $this->redis->hgetall('metavars'));
-        $this->assertEquals(array(), $this->redis->hgetall('hashDoesNotExist'));
-
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->hgetall('foo');
-        });
-    }
-
-    /* multiple databases handling commands */
-
-    function testSelectDatabase()
-    {
-        $this->assertTrue($this->redis->select(0));
-        $this->assertTrue($this->redis->select(RC::DEFAULT_DATABASE));
-
-        RC::testForServerException($this, RC::EXCEPTION_INVALID_DB_IDX, function($test) {
-            $test->redis->select(32);
-        });
-
-        RC::testForServerException($this, RC::EXCEPTION_INVALID_DB_IDX, function($test) {
-            $test->redis->select(-1);
-        });
-    }
-
-    function testMove()
-    {
-        // TODO: This test sucks big time. Period.
-        $otherDb = 5;
-        $this->redis->set('foo', 'bar');
-
-        $this->redis->select($otherDb);
-        $this->redis->flushdb();
-        $this->redis->select(RC::DEFAULT_DATABASE);
-
-        $this->assertTrue($this->redis->move('foo', $otherDb));
-        $this->assertFalse($this->redis->move('foo', $otherDb));
-        $this->assertFalse($this->redis->move('keyDoesNotExist', $otherDb));
-
-        $this->redis->set('hoge', 'piyo');
-        // TODO: shouldn't Redis send an EXCEPTION_INVALID_DB_IDX instead of EXCEPTION_OUT_OF_RANGE?
-        RC::testForServerException($this, RC::EXCEPTION_OUT_OF_RANGE, function($test) {
-            $test->redis->move('hoge', 32);
-        });
-    }
-
-    function testFlushdb()
-    {
-        $this->assertTrue($this->redis->flushdb());
-    }
-
-    /* sorting */
-
-    function testSort()
-    {
-        $unorderedList = RC::pushTailAndReturn($this->redis, 'unordered', array(2, 100, 3, 1, 30, 10));
-
-        // without parameters
-        $this->assertEquals(array(1, 2, 3, 10, 30, 100), $this->redis->sort('unordered'));
-
-        // with parameter ASC/DESC
-        $this->assertEquals(
-            array(100, 30, 10, 3, 2, 1),
-            $this->redis->sort('unordered', array(
-                'sort' => 'desc'
-            ))
-        );
-
-        // with parameter LIMIT
-        $this->assertEquals(
-            array(1, 2, 3),
-            $this->redis->sort('unordered', array(
-                'limit' => array(0, 3)
-            ))
-        );
-        $this->assertEquals(
-            array(10, 30),
-            $this->redis->sort('unordered', array(
-                'limit' => array(3, 2)
-            ))
-        );
-
-        // with parameter ALPHA
-        $this->assertEquals(
-            array(1, 10, 100, 2, 3, 30),
-            $this->redis->sort('unordered', array(
-                'alpha' => true
-            ))
-        );
-
-        // with combined parameters
-        $this->assertEquals(
-            array(30, 10, 3, 2),
-            $this->redis->sort('unordered', array(
-                'alpha' => false,
-                'sort'  => 'desc',
-                'limit' => array(1, 4)
-            ))
-        );
-
-        // with parameter ALPHA
-        $this->assertEquals(
-            array(1, 10, 100, 2, 3, 30),
-            $this->redis->sort('unordered', array(
-                'alpha' => true
-            ))
-        );
-
-        // with parameter STORE
-        $this->assertEquals(
-            count($unorderedList),
-            $this->redis->sort('unordered', array(
-                'store' => 'ordered'
-            ))
-        );
-        $this->assertEquals(array(1, 2, 3, 10, 30, 100),  $this->redis->lrange('ordered', 0, -1));
-
-        // with parameter GET
-        $this->redis->rpush('uids', 1003);
-        $this->redis->rpush('uids', 1001);
-        $this->redis->rpush('uids', 1002);
-        $this->redis->rpush('uids', 1000);
-        $sortget = array(
-            'uid:1000' => 'foo',  'uid:1001' => 'bar',
-            'uid:1002' => 'hoge', 'uid:1003' => 'piyo'
-        );
-        $this->redis->mset($sortget);
-        $this->assertEquals(
-            array_values($sortget),
-            $this->redis->sort('uids', array('get' => 'uid:*'))
-        );
-
-        // wrong type
-        RC::testForServerException($this, RC::EXCEPTION_WRONG_TYPE, function($test) {
-            $test->redis->set('foo', 'bar');
-            $test->redis->sort('foo');
-        });
-    }
-
-    /* remote server control commands */
-
-    function testInfo()
-    {
-        $serverInfo = $this->redis->info();
-
-        $this->assertInternalType('array', $serverInfo);
-        $this->assertNotNull($serverInfo['redis_version']);
-        $this->assertGreaterThan(0, $serverInfo['uptime_in_seconds']);
-        $this->assertGreaterThan(0, $serverInfo['total_connections_received']);
-    }
-
-    function testSlaveOf()
-    {
-        $masterHost = 'www.google.com';
-        $masterPort = 80;
-
-        $this->assertTrue($this->redis->slaveof($masterHost, $masterPort));
-
-        // slave of NO ONE, the implicit way
-        $this->assertTrue($this->redis->slaveof());
-
-        // slave of NO ONE, the explicit way
-        $this->assertTrue($this->redis->slaveof('NO ONE'));
-    }
-
-    /* persistence control commands */
-
-    function testSave()
-    {
-        $this->assertTrue($this->redis->save());
-    }
-
-    function testBackgroundSave()
-    {
-        $this->assertTrue($this->redis->bgsave());
-        sleep(1);
-    }
-
-    function testBackgroundRewriteAppendOnlyFile()
-    {
-        $this->assertTrue($this->redis->bgrewriteaof());
-    }
-
-    function testLastSave()
-    {
-        $this->assertGreaterThan(0, $this->redis->lastsave());
-    }
-}

+ 52 - 0
tests/PHPUnit/ArrayHasSameValuesConstraint.php

@@ -0,0 +1,52 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use \PHPUnit_Framework_Constraint;
+use \PHPUnit_Framework_ExpectationFailedException;
+
+/**
+ * Constraint that accepts arrays with the same elements but different order.
+ */
+class ArrayHasSameValuesConstraint extends PHPUnit_Framework_Constraint
+{
+    protected $array;
+
+    /**
+     * @param array $array
+     */
+    public function __construct($array)
+    {
+        $this->array = $array;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function evaluate($other)
+    {
+        if (count($this->array) !== count($other)) {
+            throw new PHPUnit_Framework_ExpectationFailedException('Failed asserting that two arrays have the same elements.');
+        }
+        if (array_diff($this->array, $other)) {
+            throw new PHPUnit_Framework_ExpectationFailedException('Failed asserting that two arrays have the same elements.');
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function toString()
+    {
+        return 'two arrays have the same elements.';
+    }
+}

+ 174 - 0
tests/PHPUnit/CommandTestCase.php

@@ -0,0 +1,174 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+use Predis\Client;
+use Predis\Profiles\ServerProfile;
+use Predis\Profiles\IServerProfile;
+
+/**
+ *
+ */
+abstract class CommandTestCase extends StandardTestCase
+{
+    /**
+     * Returns the expected command.
+     *
+     * @return ICommand|string Instance or FQN of the expected command.
+     */
+    protected abstract function getExpectedCommand();
+
+    /**
+     * Returns the expected command ID.
+     *
+     * @return string
+     */
+    protected abstract function getExpectedId();
+
+    /**
+     * Returns a new command instance.
+     *
+     * @return ICommand
+     */
+    protected function getCommand()
+    {
+        $command = $this->getExpectedCommand();
+
+        return $command instanceof ICommand ? $command : new $command();
+    }
+
+    /**
+     * Return the server profile used during the tests.
+     *
+     * @return IServerProfile
+     */
+    protected function getProfile()
+    {
+        return ServerProfile::get(REDIS_SERVER_VERSION);
+    }
+
+    /**
+     * Returns a new client instance.
+     *
+     * @param Boolean $connect Flush selected database before returning the client.
+     * @return Client
+     */
+    protected function getClient($flushdb = true)
+    {
+        $profile = $this->getProfile();
+
+        if (!$profile->supportsCommand($id = $this->getExpectedId())) {
+            $this->markTestSkipped("The profile {$profile->getVersion()} does not support command {$id}");
+        }
+
+        $parameters = array(
+            'host' => REDIS_SERVER_HOST,
+            'port' => REDIS_SERVER_PORT,
+        );
+
+        $options = array(
+            'profile' => $profile
+        );
+
+        $client = new Client($parameters, $options);
+        $client->connect();
+        $client->select(REDIS_SERVER_DBNUM);
+
+        if ($flushdb) {
+            $client->flushdb();
+        }
+
+        return $client;
+    }
+
+    /**
+     * Returns wether the command is prefixable or not.
+     *
+     * @return Boolean
+     */
+    protected function isPrefixable()
+    {
+        return $this->getCommand() instanceof IPrefixable;
+    }
+
+    /**
+     * Returns a new command instance with the specified arguments.
+     *
+     * @param ... List of arguments for the command.
+     * @return ICommand
+     */
+    protected function getCommandWithArguments(/* arguments */)
+    {
+        return $this->getCommandWithArgumentsArray(func_get_args());
+    }
+
+    /**
+     * Returns a new command instance with the specified arguments.
+     *
+     * @param array $arguments Arguments for the command.
+     * @return ICommand
+     */
+    protected function getCommandWithArgumentsArray(Array $arguments)
+    {
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        return $command;
+    }
+
+    /**
+     * Sleep the test case with microseconds resolution.
+     *
+     * @param float $seconds Seconds to sleep.
+     */
+    protected function sleep($seconds)
+    {
+        usleep($seconds * 1000000);
+    }
+
+    /**
+     * Asserts that two arrays have the same values, even if with different order.
+     *
+     * @param Array $expected Expected array.
+     * @param Array $actual Actual array.
+     */
+    protected function assertSameValues(Array $expected, Array $actual)
+    {
+        $this->assertThat($expected, new \ArrayHasSameValuesConstraint($actual));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testCommandId()
+    {
+        $command = $this->getCommand();
+
+        $this->assertInstanceOf('Predis\Commands\ICommand', $command);
+        $this->assertEquals($this->getExpectedId(), $command->getId());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testRawArguments()
+    {
+        $expected = array('1st', '2nd', '3rd', '4th');
+
+        $command = $this->getCommand();
+        $command->setRawArguments($expected);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+}

+ 387 - 0
tests/PHPUnit/ConnectionTestCase.php

@@ -0,0 +1,387 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Network;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+use Predis\ConnectionParameters;
+use Predis\Profiles\ServerProfile;
+
+/**
+ * @group realm-network
+ */
+abstract class ConnectionTestCase extends StandardTestCase
+{
+    /**
+     * @group disconnected
+     * @group slow
+     * @expectedException Predis\Network\ConnectionException
+     */
+    public function testThrowExceptionWhenUnableToConnect()
+    {
+        $parameters = array('host' => '169.254.10.10', 'connection_timeout' => 0.5);
+        $connection = $this->getConnection($profile, false, $parameters);
+        $connection->executeCommand($this->getProfile()->createCommand('ping'));
+    }
+
+    // ******************************************************************** //
+    // ---- INTEGRATION TESTS --------------------------------------------- //
+    // ******************************************************************** //
+
+    /**
+     * @group connected
+     */
+    public function testConnectForcesConnection()
+    {
+        $connection = $this->getConnection();
+
+        $this->assertFalse($connection->isConnected());
+        $connection->connect();
+        $this->assertTrue($connection->isConnected());
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ClientException
+     * @expectedExceptionMessage Connection already estabilished
+     */
+    public function testThrowsExceptionOnConnectWhenAlreadyConnected()
+    {
+        $connection = $this->getConnection();
+
+        $connection->connect();
+        $connection->connect();
+    }
+
+    /**
+     * @group connected
+     */
+    public function testDisconnectForcesDisconnection()
+    {
+        $connection = $this->getConnection();
+
+        $connection->connect();
+        $this->assertTrue($connection->isConnected());
+
+        $connection->disconnect();
+        $this->assertFalse($connection->isConnected());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testDoesNotThrowExceptionOnDisconnectWhenAlreadyDisconnected()
+    {
+        $connection = $this->getConnection();
+
+        $this->assertFalse($connection->isConnected());
+        $connection->disconnect();
+        $this->assertFalse($connection->isConnected());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testGetResourceForcesConnection()
+    {
+        $connection = $this->getConnection();
+
+        $this->assertFalse($connection->isConnected());
+        $this->assertInternalType('resource', $connection->getResource());
+        $this->assertTrue($connection->isConnected());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSendingCommandForcesConnection()
+    {
+        $connection = $this->getConnection($profile);
+        $cmdPing = $profile->createCommand('ping');
+
+        $this->assertTrue($connection->executeCommand($cmdPing));
+        $this->assertTrue($connection->isConnected());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testExecutesCommandOnServer()
+    {
+        $connection = $this->getConnection($profile);
+
+        $cmdPing = $this->getMock($profile->getCommandClass('ping'), array('parseResponse'));
+        $cmdPing->expects($this->once())
+                ->method('parseResponse')
+                ->with('PONG')
+                ->will($this->returnValue(true));
+
+        $this->assertTrue($connection->executeCommand($cmdPing));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testWritesCommandToServer()
+    {
+        $connection = $this->getConnection($profile);
+
+        $cmdPing = $this->getMock($profile->getCommandClass('ping'), array('parseResponse'));
+        $cmdPing->expects($this->never())
+                ->method('parseResponse');
+
+        $connection->writeCommand($cmdPing);
+        $connection->disconnect();
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReadsCommandFromServer()
+    {
+        $connection = $this->getConnection($profile);
+
+        $cmdPing = $this->getMock($profile->getCommandClass('ping'), array('parseResponse'));
+        $cmdPing->expects($this->once())
+                ->method('parseResponse')
+                ->with('PONG')
+                ->will($this->returnValue(true));
+
+        $connection->writeCommand($cmdPing);
+        $this->assertTrue($connection->readResponse($cmdPing));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testIsAbleToWriteMultipleCommandsAndReadThemBackForPipelining()
+    {
+        $connection = $this->getConnection($profile);
+
+        $cmdPing = $this->getMock($profile->getCommandClass('ping'), array('parseResponse'));
+        $cmdPing->expects($this->once())
+                ->method('parseResponse')
+                ->with('PONG')
+                ->will($this->returnValue(true));
+
+        $cmdEcho = $this->getMock($profile->getCommandClass('echo'), array('parseResponse'));
+        $cmdEcho->setArguments(array('ECHOED'));
+        $cmdEcho->expects($this->once())
+                ->method('parseResponse')
+                ->with('ECHOED')
+                ->will($this->returnValue('ECHOED'));
+
+        $connection = $this->getConnection();
+
+        $connection->writeCommand($cmdPing);
+        $connection->writeCommand($cmdEcho);
+
+        $this->assertTrue($connection->readResponse($cmdPing));
+        $this->assertSame('ECHOED', $connection->readResponse($cmdEcho));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSendsInitializationCommandsOnConnection()
+    {
+        $connection = $this->getConnection($profile, true);
+
+        $cmdPing = $this->getMock($profile->getCommandClass('ping'), array('parseResponse'));
+        $cmdPing->expects($this->once())
+                ->method('parseResponse')
+                ->with('PONG')
+                ->will($this->returnValue(true));
+
+        $cmdEcho = $this->getMock($profile->getCommandClass('echo'), array('parseResponse'));
+        $cmdEcho->setArguments(array('ECHOED'));
+        $cmdEcho->expects($this->once())
+                ->method('parseResponse')
+                ->with('ECHOED')
+                ->will($this->returnValue('ECHOED'));
+
+        $connection->pushInitCommand($cmdPing);
+        $connection->pushInitCommand($cmdEcho);
+
+        $connection->connect();
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReadsStatusReplies()
+    {
+        $connection = $this->getConnection($profile, true);
+
+        $connection->writeCommand($profile->createCommand('set', array('foo', 'bar')));
+        $this->assertTrue($connection->read());
+
+        $connection->writeCommand($profile->createCommand('ping'));
+        $this->assertSame('PONG', $connection->read());
+
+        $connection->writeCommand($profile->createCommand('multi'));
+        $connection->writeCommand($profile->createCommand('ping'));
+        $this->assertTrue($connection->read());
+        $this->assertInstanceOf('Predis\ResponseQueued', $connection->read());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReadsBulkReplies()
+    {
+        $connection = $this->getConnection($profile, true);
+
+        $connection->executeCommand($profile->createCommand('set', array('foo', 'bar')));
+
+        $connection->writeCommand($profile->createCommand('get', array('foo')));
+        $this->assertSame('bar', $connection->read());
+
+        $connection->writeCommand($profile->createCommand('get', array('hoge')));
+        $this->assertNull($connection->read());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReadsIntegerReplies()
+    {
+        $connection = $this->getConnection($profile, true);
+
+        $connection->executeCommand($profile->createCommand('rpush', array('metavars', 'foo', 'hoge', 'lol')));
+        $connection->writeCommand($profile->createCommand('llen', array('metavars')));
+
+        $this->assertSame(3, $connection->read());
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testReadsErrorRepliesAsExceptions()
+    {
+        $connection = $this->getConnection($profile, true, array('throw_errors' => true));
+
+        $connection->executeCommand($profile->createCommand('set', array('foo', 'bar')));
+        $connection->writeCommand($profile->createCommand('rpush', array('foo', 'baz')));
+
+        $connection->read();
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReadsErrorRepliesAsResponseErrorObjects()
+    {
+        $connection = $this->getConnection($profile, true, array('throw_errors' => false));
+
+        $connection->executeCommand($profile->createCommand('set', array('foo', 'bar')));
+        $connection->writeCommand($profile->createCommand('rpush', array('foo', 'baz')));
+
+        $this->assertInstanceOf('Predis\ResponseError', $error = $connection->read());
+        $this->assertSame('ERR Operation against a key holding the wrong kind of value', $error->getMessage());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReadsMultibulkRepliesAsArrays()
+    {
+        $connection = $this->getConnection($profile, true);
+
+        $connection->executeCommand($profile->createCommand('rpush', array('metavars', 'foo', 'hoge', 'lol')));
+        $connection->writeCommand($profile->createCommand('lrange', array('metavars', 0, -1)));
+
+        $this->assertSame(array('foo', 'hoge', 'lol'), $connection->read());
+    }
+
+    /**
+     * @group connected
+     * @group slow
+     * @expectedException Predis\Network\ConnectionException
+     * @expectedExceptionMessage Connection timed out
+     */
+    public function testThrowsExceptionOnConnectionTimeout()
+    {
+        $connection = $this->getConnection($_, false, array('host' => '169.254.10.10', 'connection_timeout' => 0.5));
+
+        $connection->connect();
+    }
+
+    /**
+     * @group connected
+     * @group slow
+     * @expectedException Predis\Network\ConnectionException
+     */
+    public function testThrowsExceptionOnReadWriteTimeout()
+    {
+        $connection = $this->getConnection($profile, true, array('read_write_timeout' => 0.5));
+
+        $connection->executeCommand($profile->createCommand('brpop', array('foo', 3)));
+    }
+
+    // ******************************************************************** //
+    // ---- HELPER METHODS ------------------------------------------------ //
+    // ******************************************************************** //
+
+    /**
+     * Returns a named array with the default connection parameters and their values.
+     *
+     * @return Array Default connection parameters.
+     */
+    protected function getDefaultParametersArray()
+    {
+        return array(
+            'scheme' => 'tcp',
+            'host' => REDIS_SERVER_HOST,
+            'port' => REDIS_SERVER_PORT,
+            'database' => REDIS_SERVER_DBNUM,
+            'read_write_timeout' => 2,
+        );
+    }
+
+    /**
+     * Returns a new instance of connection parameters.
+     *
+     * @param array $additional Additional connection parameters.
+     * @return ConnectionParameters Default connection parameters.
+     */
+    protected function getParameters($additional = array())
+    {
+        $parameters = array_merge($this->getDefaultParametersArray(), $additional);
+        $parameters = new ConnectionParameters($parameters);
+
+        return $parameters;
+    }
+
+    /**
+     * Returns a new instance of server profile.
+     *
+     * @param array $additional Additional connection parameters.
+     * @return ServerProfile
+     */
+    protected function getProfile($version = null)
+    {
+        return ServerProfile::get($version ?: REDIS_SERVER_VERSION);
+    }
+
+    /**
+     * Returns a new instance of a connection instance.
+     *
+     * @param ServerProfile $profile Reference to the server profile instance.
+     * @param Boolean $initialize Push default initialization commands (SELECT and FLUSHDB).
+     * @param array $parameters Additional connection parameters.
+     * @return StreamConnection
+     */
+    protected abstract function getConnection(&$profile = null, $initialize = false, Array $parameters = array());
+}

+ 67 - 0
tests/PHPUnit/DistributionStrategyTestCase.php

@@ -0,0 +1,67 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Distribution;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ *
+ */
+abstract class DistributionStrategyTestCase extends StandardTestCase
+{
+    /**
+     * Returns a new instance of the tested distributor.
+     *
+     * @return Predis\Distribution\IDistributionStrategy
+     */
+    protected abstract function getDistributorInstance();
+
+    /**
+     * Returns a list of nodes from the hashring.
+     *
+     * @param IDistributionStrategy $ring Hashring instance.
+     * @param int $iterations Number of nodes to fetch.
+     * @return array Nodes from the hashring.
+     */
+    protected function getNodes(IDistributionStrategy $ring, $iterations = 10)
+    {
+        $nodes = array();
+
+        for ($i = 0; $i < $iterations; $i++) {
+            $key = $ring->generateKey($i * $i);
+            $nodes[] = $ring->get($key);
+        }
+
+        return $nodes;
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testEmptyRingThrowsException()
+    {
+        $this->setExpectedException('Predis\Distribution\EmptyRingException');
+
+        $ring = $this->getDistributorInstance();
+        $ring->get('nodekey');
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testRemoveOnEmptyRingDoesNotThrowException()
+    {
+        $ring = $this->getDistributorInstance();
+
+        $this->assertNull($ring->remove('node'));
+    }
+}

+ 77 - 0
tests/PHPUnit/ServerVersionTestCase.php

@@ -0,0 +1,77 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Profiles;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ *
+ */
+abstract class ServerVersionTestCase extends StandardTestCase
+{
+    /**
+     * Returns a new instance of the tested profile.
+     *
+     * @return Predis\Profiles\IServerProfile
+     */
+    protected abstract function getProfileInstance();
+
+    /**
+     * Returns the expected version string for the tested profile.
+     *
+     * @return string Version string.
+     */
+    protected abstract function getExpectedVersion();
+
+    /**
+     * Returns the expected list of commands supported by the tested profile.
+     *
+     * @return array List of supported commands.
+     */
+    protected abstract function getExpectedCommands();
+
+    /**
+     * Returns the list of commands supported by the current
+     * server profile.
+     *
+     * @param IServerProfile $profile Server profile instance.
+     * @return array
+     */
+    protected function getCommands(IServerProfile $profile)
+    {
+        $commands = $profile->getSupportedCommands();
+
+        return array_keys($commands);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testGetVersion()
+    {
+        $profile = $this->getProfileInstance();
+
+        $this->assertEquals($this->getExpectedVersion(), $profile->getVersion());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testSupportedCommands()
+    {
+        $profile = $this->getProfileInstance();
+        $expected = $this->getExpectedCommands();
+        $commands = $this->getCommands($profile);
+
+        $this->assertSame($expected, $commands);
+    }
+}

+ 43 - 0
tests/Predis/ClientExceptionTest.php

@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ *
+ */
+class ClientExceptionTest extends StandardTestCase
+{
+    /**
+     * @group disconnected
+     */
+    public function testExceptionMessage()
+    {
+        $message = 'This is a client exception.';
+
+        $this->setExpectedException('Predis\ClientException', $message);
+
+        throw new ClientException($message);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testExceptionClass()
+    {
+        $exception = new ClientException();
+
+        $this->assertInstanceOf('Predis\ClientException', $exception);
+        $this->assertInstanceOf('Predis\PredisException', $exception);
+    }
+}

+ 647 - 0
tests/Predis/ClientTest.php

@@ -0,0 +1,647 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+use Predis\Profiles\ServerProfile;
+use Predis\Network\PredisCluster;
+
+/**
+ *
+ */
+class ClientTest extends StandardTestCase
+{
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithoutArguments()
+    {
+        $client = new Client();
+
+        $connection = $client->getConnection();
+        $this->assertInstanceOf('Predis\Network\IConnectionSingle', $connection);
+
+        $parameters = $connection->getParameters();
+        $this->assertSame($parameters->host, '127.0.0.1');
+        $this->assertSame($parameters->port, 6379);
+
+        $options = $client->getOptions();
+        $this->assertSame($options->profile->getVersion(), ServerProfile::getDefault()->getVersion());
+
+        $this->assertFalse($client->isConnected());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithNullArgument()
+    {
+        $client = new Client(null);
+
+        $connection = $client->getConnection();
+        $this->assertInstanceOf('Predis\Network\IConnectionSingle', $connection);
+
+        $parameters = $connection->getParameters();
+        $this->assertSame($parameters->host, '127.0.0.1');
+        $this->assertSame($parameters->port, 6379);
+
+        $options = $client->getOptions();
+        $this->assertSame($options->profile->getVersion(), ServerProfile::getDefault()->getVersion());
+
+        $this->assertFalse($client->isConnected());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithNullAndNullArguments()
+    {
+        $client = new Client(null, null);
+
+        $connection = $client->getConnection();
+        $this->assertInstanceOf('Predis\Network\IConnectionSingle', $connection);
+
+        $parameters = $connection->getParameters();
+        $this->assertSame($parameters->host, '127.0.0.1');
+        $this->assertSame($parameters->port, 6379);
+
+        $options = $client->getOptions();
+        $this->assertSame($options->profile->getVersion(), ServerProfile::getDefault()->getVersion());
+
+        $this->assertFalse($client->isConnected());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithArrayArgument()
+    {
+        $client = new Client($arg1 = array('host' => 'localhost', 'port' => 7000));
+
+        $parameters = $client->getConnection()->getParameters();
+        $this->assertSame($parameters->host, $arg1['host']);
+        $this->assertSame($parameters->port, $arg1['port']);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithArrayOfArrayArgument()
+    {
+        $arg1 = array(
+            array('host' => 'localhost', 'port' => 7000),
+            array('host' => 'localhost', 'port' => 7001),
+        );
+
+        $client = new Client($arg1);
+
+        $this->assertInstanceOf('Predis\Network\IConnectionCluster', $client->getConnection());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithStringArgument()
+    {
+        $client = new Client('tcp://localhost:7000');
+
+        $parameters = $client->getConnection()->getParameters();
+        $this->assertSame($parameters->host, 'localhost');
+        $this->assertSame($parameters->port, 7000);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithArrayOfStringArgument()
+    {
+        $client = new Client($arg1 = array('tcp://localhost:7000', 'tcp://localhost:7001'));
+
+        $this->assertInstanceOf('Predis\Network\IConnectionCluster', $client->getConnection());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithArrayOfConnectionsArgument()
+    {
+        $connection1 = $this->getMock('Predis\Network\IConnectionSingle');
+        $connection2 = $this->getMock('Predis\Network\IConnectionSingle');
+
+        $client = new Client(array($connection1, $connection2));
+
+        $this->assertInstanceOf('Predis\Network\IConnectionCluster', $cluster = $client->getConnection());
+        $this->assertSame($connection1, $cluster->getConnectionById(0));
+        $this->assertSame($connection2, $cluster->getConnectionById(1));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithConnectionArgument()
+    {
+        $factory = new ConnectionFactory();
+        $connection = $factory->create('tcp://localhost:7000');
+
+        $client = new Client($connection);
+
+        $this->assertInstanceOf('Predis\Network\IConnectionSingle', $client->getConnection());
+        $this->assertSame($connection, $client->getConnection());
+
+        $parameters = $client->getConnection()->getParameters();
+        $this->assertSame($parameters->host, 'localhost');
+        $this->assertSame($parameters->port, 7000);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithClusterArgument()
+    {
+        $cluster = new PredisCluster();
+
+        $factory = new ConnectionFactory();
+        $factory->createCluster($cluster, array('tcp://localhost:7000', 'tcp://localhost:7001'));
+
+        $client = new Client($cluster);
+
+        $this->assertInstanceOf('Predis\Network\IConnectionCluster', $client->getConnection());
+        $this->assertSame($cluster, $client->getConnection());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithNullAndStringArgument()
+    {
+        $client = new Client(null, '2.0');
+
+        $this->assertSame($client->getProfile()->getVersion(), ServerProfile::get('2.0')->getVersion());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithNullAndProfileArgument()
+    {
+        $client = new Client(null, $arg2 = ServerProfile::get('2.0'));
+
+        $this->assertSame($client->getProfile(), $arg2);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConstructorWithNullAndArrayArgument()
+    {
+        $factory = $this->getMock('Predis\IConnectionFactory');
+
+        $arg2 = array('profile' => '2.0', 'prefix' => 'prefix:', 'connections' => $factory);
+        $client = new Client(null, $arg2);
+
+        $profile = $client->getProfile();
+        $this->assertSame($profile->getVersion(), ServerProfile::get('2.0')->getVersion());
+        $this->assertInstanceOf('Predis\Commands\Processors\KeyPrefixProcessor', $profile->getProcessor());
+        $this->assertSame('prefix:', $profile->getProcessor()->getPrefix());
+
+        $this->assertSame($factory, $client->getConnectionFactory());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testConnectAndDisconnect()
+    {
+        $connection = $this->getMock('Predis\Network\IConnection');
+        $connection->expects($this->once())->method('connect');
+        $connection->expects($this->once())->method('disconnect');
+
+        $client = new Client($connection);
+        $client->connect();
+        $client->disconnect();
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIsConnectedChecksConnectionState()
+    {
+        $connection = $this->getMock('Predis\Network\IConnection');
+        $connection->expects($this->once())->method('isConnected');
+
+        $client = new Client($connection);
+        $client->isConnected();
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testQuitIsAliasForDisconnect()
+    {
+        $connection = $this->getMock('Predis\Network\IConnection');
+        $connection->expects($this->once())->method('disconnect');
+
+        $client = new Client($connection);
+        $client->quit();
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testCreatesNewCommandUsingSpecifiedProfile()
+    {
+        $ping = ServerProfile::getDefault()->createCommand('ping', array());
+
+        $profile = $this->getMock('Predis\Profiles\IServerProfile');
+        $profile->expects($this->once())
+                ->method('createCommand')
+                ->with('ping', array())
+                ->will($this->returnValue($ping));
+
+        $client = new Client(null, $profile);
+        $this->assertSame($ping, $client->createCommand('ping', array()));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testExecuteCommand()
+    {
+        $ping = ServerProfile::getDefault()->createCommand('ping', array());
+
+        $connection= $this->getMock('Predis\Network\IConnection');
+        $connection->expects($this->once())
+                   ->method('executeCommand')
+                   ->with($ping)
+                   ->will($this->returnValue(true));
+
+        $client = new Client($connection);
+
+        $this->assertTrue($client->executeCommand($ping));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testExecuteCommandOnEachNode()
+    {
+        $ping = ServerProfile::getDefault()->createCommand('ping', array());
+
+        $connection1 = $this->getMock('Predis\Network\IConnectionSingle');
+        $connection1->expects($this->once())
+                    ->method('executeCommand')
+                    ->with($ping)
+                    ->will($this->returnValue(true));
+
+        $connection2 = $this->getMock('Predis\Network\IConnectionSingle');
+        $connection2->expects($this->once())
+                    ->method('executeCommand')
+                    ->with($ping)
+                    ->will($this->returnValue(false));
+
+        $client = new Client(array($connection1, $connection2));
+
+        $this->assertSame(array(true, false), $client->executeCommandOnShards($ping));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testExecuteCommandOnEachNodeButConnectionSingle()
+    {
+        $ping = ServerProfile::getDefault()->createCommand('ping', array());
+
+        $connection = $this->getMock('Predis\Network\IConnectionSingle');
+        $connection->expects($this->once())
+                    ->method('executeCommand')
+                    ->with($ping)
+                    ->will($this->returnValue(true));
+
+        $client = new Client($connection);
+
+        $this->assertSame(array(true), $client->executeCommandOnShards($ping));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testCallingRedisCommandExecutesInstanceOfCommand()
+    {
+        $ping = ServerProfile::getDefault()->createCommand('ping', array());
+
+        $connection = $this->getMock('Predis\Network\IConnection');
+        $connection->expects($this->once())
+                   ->method('executeCommand')
+                   ->with($this->isInstanceOf('Predis\Commands\ConnectionPing'))
+                   ->will($this->returnValue(true));
+
+        $profile = $this->getMock('Predis\Profiles\IServerProfile');
+        $profile->expects($this->once())
+                ->method('createCommand')
+                ->with('ping', array())
+                ->will($this->returnValue($ping));
+
+        $client = $this->getMock('Predis\Client', array('createCommand'), array($connection, $profile));
+
+        $this->assertTrue($client->ping());
+    }
+
+    /**
+     * @group disconnected
+     * @expectedException Predis\ClientException
+     * @expectedExceptionMessage 'invalidcommand' is not a registered Redis command
+     */
+    public function testThrowsExceptionOnNonRegisteredRedisCommand()
+    {
+        $client = new Client();
+        $client->invalidCommand();
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testGetConnectionFromClusterWithAlias()
+    {
+        $client = new Client(array('tcp://host1?alias=node01', 'tcp://host2?alias=node02'));
+
+        $this->assertInstanceOf('Predis\Network\IConnectionCluster', $cluster = $client->getConnection());
+        $this->assertInstanceOf('Predis\Network\IConnectionSingle', $node01 = $client->getConnection('node01'));
+        $this->assertInstanceOf('Predis\Network\IConnectionSingle', $node02 = $client->getConnection('node02'));
+
+        $this->assertSame('host1', $node01->getParameters()->host);
+        $this->assertSame('host2', $node02->getParameters()->host);
+    }
+
+    /**
+     * @group disconnected
+     * @expectedException Predis\NotSupportedException
+     * @expectedExceptionMessage Retrieving connections by alias is supported only with clustered connections
+     */
+    public function testGetConnectionWithAliasWorksOnlyWithCluster()
+    {
+        $client = new Client();
+
+        $client->getConnection('node01');
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPipelineWithoutArgumentsReturnsPipelineContext()
+    {
+        $client = new Client();
+
+        $this->assertInstanceOf('Predis\Pipeline\PipelineContext', $pipeline = $client->pipeline());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPipelineWithArrayReturnsPipelineContextWithOptions()
+    {
+        $client = new Client();
+
+        $executor = $this->getMock('Predis\Pipeline\IPipelineExecutor');
+        $options = array('executor' => $executor);
+
+        $this->assertInstanceOf('Predis\Pipeline\PipelineContext', $pipeline = $client->pipeline($options));
+
+        $reflection = new \ReflectionProperty($pipeline, 'executor');
+        $reflection->setAccessible(true);
+
+        $this->assertSame($executor, $reflection->getValue($pipeline));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPipelineWithCallableExecutesPipeline()
+    {
+        $callable = $this->getMock('stdClass', array('__invoke'));
+        $callable->expects($this->once())
+                 ->method('__invoke')
+                 ->with($this->isInstanceOf('Predis\Pipeline\PipelineContext'));
+
+        $client = new Client();
+        $client->pipeline($callable);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPipelineWithArrayAndCallableExecutesPipelineWithOptions()
+    {
+        $executor = $this->getMock('Predis\Pipeline\IPipelineExecutor');
+        $options = array('executor' => $executor);
+
+        $test = $this;
+        $mockCallback = function($pipeline) use($executor, $test) {
+            $reflection = new \ReflectionProperty($pipeline, 'executor');
+            $reflection->setAccessible(true);
+
+            $test->assertSame($executor, $reflection->getValue($pipeline));
+        };
+
+        $callable = $this->getMock('stdClass', array('__invoke'));
+        $callable->expects($this->once())
+                 ->method('__invoke')
+                 ->with($this->isInstanceOf('Predis\Pipeline\PipelineContext'))
+                 ->will($this->returnCallback($mockCallback));
+
+        $client = new Client();
+        $client->pipeline($options, $callable);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPubSubWithoutArgumentsReturnsPubSubContext()
+    {
+        $client = new Client();
+
+        $this->assertInstanceOf('Predis\PubSub\PubSubContext', $pubsub = $client->pubSub());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPubSubWithArrayReturnsPubSubContextWithOptions()
+    {
+        $connection = $this->getMock('Predis\Network\IConnectionSingle');
+        $options = array('subscribe' => 'channel');
+
+        $client = new Client($connection);
+
+        $this->assertInstanceOf('Predis\PubSub\PubSubContext', $pubsub = $client->pubSub($options));
+
+        $reflection = new \ReflectionProperty($pubsub, 'options');
+        $reflection->setAccessible(true);
+
+        $this->assertSame($options, $reflection->getValue($pubsub));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPubSubWithArrayAndCallableExecutesPubSub()
+    {
+        // NOTE: we use a subscribe count of 0 in the fake message to trick
+        //       the context and to make it think that it can be closed
+        //       since there are no more subscriptions active.
+
+        $message = array('subscribe', 'channel', 0);
+        $options = array('subscribe' => 'channel');
+
+        $connection = $this->getMock('Predis\Network\IConnectionSingle');
+        $connection->expects($this->once())
+                   ->method('read')
+                   ->will($this->returnValue($message));
+
+        $callable = $this->getMock('stdClass', array('__invoke'));
+        $callable->expects($this->once())
+                 ->method('__invoke');
+
+        $client = new Client($connection);
+        $client->pubSub($options, $callable);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testMultiExecWithoutArgumentsReturnsMultiExecContext()
+    {
+        $client = new Client();
+
+        $this->assertInstanceOf('Predis\Transaction\MultiExecContext', $pubsub = $client->multiExec());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testMultiExecWithArrayReturnsMultiExecContextWithOptions()
+    {
+        $options = array('cas' => true, 'retry' => 3);
+
+        $client = new Client();
+
+        $this->assertInstanceOf('Predis\Transaction\MultiExecContext', $tx = $client->multiExec($options));
+
+        $reflection = new \ReflectionProperty($tx, 'options');
+        $reflection->setAccessible(true);
+
+        $this->assertSame($options, $reflection->getValue($tx));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testMultiExecWithArrayAndCallableExecutesMultiExec()
+    {
+        // NOTE: we use CAS since testing the actual MULTI/EXEC context
+        //       here is not the point.
+        $options = array('cas' => true, 'retry' => 3);
+
+        $connection = $this->getMock('Predis\Network\IConnectionSingle');
+        $connection->expects($this->once())
+                   ->method('executeCommand')
+                   ->will($this->returnValue(new ResponseQueued()));
+
+        $txCallback = function($tx) { $tx->ping(); };
+
+        $callable = $this->getMock('stdClass', array('__invoke'));
+        $callable->expects($this->once())
+                 ->method('__invoke')
+                 ->will($this->returnCallback($txCallback));
+
+        $client = new Client($connection);
+        $client->multiExec($options, $callable);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testMonitorReturnsMonitorContext()
+    {
+        $connection = $this->getMock('Predis\Network\IConnectionSingle');
+        $client = new Client($connection);
+
+        $this->assertInstanceOf('Predis\MonitorContext', $monitor = $client->monitor());
+    }
+
+    // ******************************************************************** //
+    // ---- HELPER METHODS ------------------------------------------------ //
+    // ******************************************************************** //
+
+    /**
+     * Returns a named array with the default connection parameters and their values.
+     *
+     * @return Array Default connection parameters.
+     */
+    protected function getDefaultParametersArray()
+    {
+        return array(
+            'scheme' => 'tcp',
+            'host' => REDIS_SERVER_HOST,
+            'port' => REDIS_SERVER_PORT,
+            'database' => REDIS_SERVER_DBNUM,
+        );
+    }
+
+    /**
+     * Returns a named array with the default client options and their values.
+     *
+     * @return Array Default connection parameters.
+     */
+    protected function getDefaultOptionsArray()
+    {
+        return array(
+            'profile' => REDIS_SERVER_VERSION,
+        );
+    }
+
+    /**
+     * Returns a named array with the default connection parameters merged with
+     * the specified additional parameters.
+     *
+     * @param Array $additional Additional connection parameters.
+     * @return Array Connection parameters.
+     */
+    protected function getParametersArray(Array $additional)
+    {
+        return array_merge($this->getDefaultParametersArray(), $additional);
+    }
+
+    /**
+     * Returns an URI string representation of the specified connection parameters.
+     *
+     * @param Array $parameters Array of connection parameters.
+     * @return String URI string.
+     */
+    protected function getParametersString(Array $parameters)
+    {
+        $defaults = $this->getDefaultParametersArray();
+
+        $scheme = isset($parameters['scheme']) ? $parameters['scheme'] : $defaults['scheme'];
+        $host = isset($parameters['host']) ? $parameters['host'] : $defaults['host'];
+        $port = isset($parameters['port']) ? $parameters['port'] : $defaults['port'];
+
+        unset($parameters['scheme'], $parameters['host'], $parameters['port']);
+        $uriString = "$scheme://$host:$port/?";
+
+        foreach ($parameters as $k => $v) {
+            $uriString .= "$k=$v&";
+        }
+
+        return $uriString;
+    }
+}

+ 216 - 0
tests/Predis/Commands/CommandTest.php

@@ -0,0 +1,216 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ *
+ */
+class CommandTest extends StandardTestCase
+{
+    /**
+     * @group disconnected
+     */
+    public function testImplementsCorrectInterface()
+    {
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+
+        $this->assertInstanceOf('Predis\Commands\ICommand', $command);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testGetEmptyArguments()
+    {
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+
+        $this->assertEmpty($command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testSetRawArguments()
+    {
+        $arguments = array('1st', '2nd', '3rd');
+
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+        $command->setRawArguments($arguments);
+
+        $this->assertEquals($arguments, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     *
+     * @todo Since Command::filterArguments is protected we cannot set an expectation
+     *       for it when Command::setArguments() is invoked. I wonder how we can do that.
+     */
+    public function testSetArguments()
+    {
+        $arguments = array('1st', '2nd', '3rd');
+
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+        $command->setArguments($arguments);
+
+        $this->assertEquals($arguments, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testGetArgumentAtIndex()
+    {
+        $arguments = array('1st', '2nd', '3rd');
+
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+        $command->setArguments($arguments);
+
+        $this->assertEquals($arguments[0], $command->getArgument(0));
+        $this->assertEquals($arguments[2], $command->getArgument(2));
+        $this->assertNull($command->getArgument(10));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $response = 'response-buffer';
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+
+        $this->assertEquals($response, $command->parseResponse($response));
+    }
+
+    /**
+     * @group disconnected
+     * @protected
+     */
+    public function testCheckSameHashForKeys()
+    {
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+
+        $checkSameHashForKeys = new \ReflectionMethod($command, 'checkSameHashForKeys');
+        $checkSameHashForKeys->setAccessible(true);
+
+        $this->assertTrue($checkSameHashForKeys->invoke($command, array('foo', '{foo}:bar')));
+        $this->assertFalse($checkSameHashForKeys->invoke($command, array('foo', '{foo}:bar', 'foo:bar')));
+    }
+
+    /**
+     * @group disconnected
+     * @protected
+     */
+    public function testCanBeHashed()
+    {
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+
+        $canBeHashed = new \ReflectionMethod($command, 'canBeHashed');
+        $canBeHashed->setAccessible(true);
+
+        $this->assertFalse($canBeHashed->invoke($command));
+
+        $command->setRawArguments(array('key'));
+        $this->assertTrue($canBeHashed->invoke($command));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testDoesNotReturnAnHashByDefault()
+    {
+        $distributor = $this->getMock('Predis\Distribution\INodeKeyGenerator');
+        $distributor->expects($this->never())->method('generateKey');
+
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+
+        $command->getHash($distributor);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testReturnAnHashWhenCanBeHashedAndCachesIt()
+    {
+        $key = 'key';
+        $hash = "$key-hash";
+
+        $distributor = $this->getMock('Predis\Distribution\INodeKeyGenerator');
+        $distributor->expects($this->once())
+                    ->method('generateKey')
+                    ->with($key)
+                    ->will($this->returnValue($hash));
+
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+        $command->setRawArguments(array($key));
+
+        $this->assertEquals($hash, $command->getHash($distributor));
+
+        $this->assertEquals($hash, $command->getHash($distributor));
+        $this->assertEquals($hash, $command->getHash($distributor));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testExtractsKeyTagsBeforeHashing()
+    {
+        $tag = 'key';
+        $key = "{{$tag}}:ignore";
+        $hash = "$tag-hash";
+
+        $distributor = $this->getMock('Predis\Distribution\INodeKeyGenerator');
+        $distributor->expects($this->once())
+                    ->method('generateKey')
+                    ->with($tag)
+                    ->will($this->returnValue($hash));
+
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+        $command->setRawArguments(array($key));
+
+        $this->assertEquals($hash, $command->getHash($distributor));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testToString()
+    {
+        $expected = 'SET key value';
+        $arguments = array('key', 'value');
+
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+        $command->expects($this->once())->method('getId')->will($this->returnValue('SET'));
+
+        $command->setRawArguments($arguments);
+
+        $this->assertEquals($expected, (string) $command);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testToStringWithLongArguments()
+    {
+        $expected = 'SET key abcdefghijklmnopqrstuvwxyz012345[...]';
+        $arguments = array('key', 'abcdefghijklmnopqrstuvwxyz0123456789');
+
+        $command = $this->getMockForAbstractClass('Predis\Commands\Command');
+        $command->expects($this->once())->method('getId')->will($this->returnValue('SET'));
+
+        $command->setRawArguments($arguments);
+
+        $this->assertEquals($expected, (string) $command);
+    }
+}

+ 64 - 0
tests/Predis/Commands/ConnectionAuthTest.php

@@ -0,0 +1,64 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-connection
+ */
+class ConnectionAuthTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ConnectionAuth';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'AUTH';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('password');
+        $expected = array('password');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = null;
+        $expected = null;
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+}

+ 76 - 0
tests/Predis/Commands/ConnectionEchoTest.php

@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-connection
+ */
+class ConnectionEchoTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ConnectionEcho';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'ECHO';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('message');
+        $expected = array('message');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = 'message';
+        $expected = 'message';
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testAlwaysReturnsThePassedMessage()
+    {
+        $redis = $this->getClient();
+
+        $message = 'Can you hear me?';
+
+        $this->assertSame($message, $redis->echo($message));
+    }
+}

+ 71 - 0
tests/Predis/Commands/ConnectionPingTest.php

@@ -0,0 +1,71 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-connection
+ */
+class ConnectionPingTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ConnectionPing';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'PING';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array();
+        $expected = array();
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse('PONG'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testAlwaysReturnsTrue()
+    {
+        $redis = $this->getClient();
+
+        $this->assertTrue($redis->ping());
+    }
+}

+ 72 - 0
tests/Predis/Commands/ConnectionQuitTest.php

@@ -0,0 +1,72 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-connection
+ */
+class ConnectionQuitTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ConnectionQuit';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'QUIT';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array();
+        $expected = array();
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(true));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsTrueWhenClosingConnection()
+    {
+        $redis = $this->getClient();
+        $command = $this->getCommand();
+
+        $this->assertTrue($redis->executeCommand($command));
+    }
+}

+ 86 - 0
tests/Predis/Commands/ConnectionSelectTest.php

@@ -0,0 +1,86 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-connection
+ */
+class ConnectionSelectTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ConnectionSelect';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'SELECT';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array(10);
+        $expected = array(10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(true));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testCanSelectDifferentDatabase()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->select(REDIS_SERVER_DBNUM - 1));
+        $this->assertFalse($redis->exists('foo'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR invalid DB index
+     */
+    public function testThrowsExceptionOnUnexpectedDatabase()
+    {
+        $redis = $this->getClient();
+
+        $redis->select(100000000);
+    }
+}

+ 114 - 0
tests/Predis/Commands/HashDeleteTest.php

@@ -0,0 +1,114 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashDeleteTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashDelete';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HDEL';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'field1', 'field2', 'field3');
+        $expected = array('key', 'field1', 'field2', 'field3');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArgumentsFieldsAsSingleArray()
+    {
+        $arguments = array('key', array('field1', 'field2', 'field3'));
+        $expected = array('key', 'field1', 'field2', 'field3');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame(1, $this->getCommand()->parseResponse(1));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'field1', 'field2', 'field3');
+        $expected = array('prefix:key', 'field1', 'field2', 'field3');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testDeletesSpecifiedFieldsFromHash()
+    {
+        $redis = $this->getClient();
+
+        $redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo', 'lol', 'wut');
+
+        $this->assertSame(2, $redis->hdel('metavars', 'foo', 'hoge'));
+        $this->assertSame(0, $redis->hdel('metavars', 'foofoo'));
+        $this->assertSame(0, $redis->hdel('unknown', 'foo'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hdel('foo', 'bar');
+    }
+}

+ 103 - 0
tests/Predis/Commands/HashExistsTest.php

@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashExistsTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashExists';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HEXISTS';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'field');
+        $expected = array('key', 'field');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertFalse($command->parseResponse(0));
+        $this->assertTrue($command->parseResponse(1));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'field');
+        $expected = array('prefix:key', 'field');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsExistenceOfSpecifiedField()
+    {
+        $redis = $this->getClient();
+
+        $redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo');
+
+        $this->assertTrue($redis->hexists('metavars', 'foo'));
+        $this->assertFalse($redis->hexists('metavars', 'lol'));
+        $this->assertFalse($redis->hexists('unknown', 'foo'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hexists('foo', 'bar');
+    }
+}

+ 104 - 0
tests/Predis/Commands/HashGetAllTest.php

@@ -0,0 +1,104 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashGetAllTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashGetAll';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HGETALL';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = array('foo', 'bar', 'hoge', 'piyo', 'lol', 'wut');
+        $expected = array('foo' => 'bar', 'hoge' => 'piyo', 'lol' => 'wut');
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key');
+        $expected = array('prefix:key');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsAllTheFieldsAndTheirValues()
+    {
+        $redis = $this->getClient();
+
+        $redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo', 'lol', 'wut');
+
+        $this->assertSame(array('foo' => 'bar', 'hoge' => 'piyo', 'lol' => 'wut'), $redis->hgetall('metavars'));
+        $this->assertSame(array(), $redis->hgetall('unknown'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hgetall('foo');
+    }
+}

+ 120 - 0
tests/Predis/Commands/HashGetMultipleTest.php

@@ -0,0 +1,120 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashGetMultipleTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashGetMultiple';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HMGET';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'field1', 'field2', 'field3');
+        $expected = array('key', 'field1', 'field2', 'field3');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArgumentsFieldsAsSingleArray()
+    {
+        $arguments = array('key', array('field1', 'field2', 'field3'));
+        $expected = array('key', 'field1', 'field2', 'field3');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = array('bar', 'piyo', 'wut');
+        $expected = array('bar', 'piyo', 'wut');
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'field1', 'field2', 'field3');
+        $expected = array('prefix:key', 'field1', 'field2', 'field3');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsValuesOfSpecifiedFields()
+    {
+        $redis = $this->getClient();
+
+        $redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo', 'lol', 'wut');
+
+        $this->assertSame(array('bar', 'piyo', null), $redis->hmget('metavars', 'foo', 'hoge', 'unknown'));
+        $this->assertSame(array('bar', 'bar'), $redis->hmget('metavars', 'foo', 'foo'));
+        $this->assertSame(array(null, null), $redis->hmget('metavars', 'unknown', 'unknown'));
+        $this->assertSame(array(null, null), $redis->hmget('unknown', 'foo', 'hoge'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hmget('foo', 'bar');
+    }
+}

+ 100 - 0
tests/Predis/Commands/HashGetTest.php

@@ -0,0 +1,100 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashGetTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashGet';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HGET';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'field');
+        $expected = array('key', 'field');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame('value', $this->getCommand()->parseResponse('value'));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'field');
+        $expected = array('prefix:key', 'field');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsValueOfSpecifiedField()
+    {
+        $redis = $this->getClient();
+
+        $redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo');
+
+        $this->assertSame('bar', $redis->hget('metavars', 'foo'));
+        $this->assertNull($redis->hget('metavars', 'lol'));
+        $this->assertNull($redis->hget('unknown', 'foo'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hget('foo', 'bar');
+    }
+}

+ 125 - 0
tests/Predis/Commands/HashIncrementByFloatTest.php

@@ -0,0 +1,125 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashIncrementByFloatTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashIncrementByFloat';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HINCRBYFLOAT';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'field', 10.5);
+        $expected = array('key', 'field', 10.5);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame(10.5, $this->getCommand()->parseResponse(10.5));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'field', 10.5);
+        $expected = array('prefix:key', 'field', 10.5);
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testIncrementsValueOfFieldByFloat()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame('10.5', $redis->hincrbyfloat('metavars', 'foo', 10.5));
+        $this->assertSame('10.001', $redis->hincrbyfloat('metavars', 'hoge', 10.001));
+        $this->assertSame('11', $redis->hincrbyfloat('metavars', 'hoge', 0.999));
+        $this->assertSame(array('foo' => '10.5', 'hoge' => '11'), $redis->hgetall('metavars'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testDecrementsValueOfFieldByFloat()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame('-10.5', $redis->hincrbyfloat('metavars', 'foo', -10.5));
+        $this->assertSame('-10.001', $redis->hincrbyfloat('metavars', 'hoge', -10.001));
+        $this->assertSame('-11', $redis->hincrbyfloat('metavars', 'hoge', -0.999));
+        $this->assertSame(array('foo' => '-10.5', 'hoge' => '-11'), $redis->hgetall('metavars'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR hash value is not a valid float
+     */
+    public function testThrowsExceptionOnStringField()
+    {
+        $redis = $this->getClient();
+
+        $redis->hset('metavars', 'foo', 'bar');
+        $redis->hincrbyfloat('metavars', 'foo', 10.0);
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hincrbyfloat('foo', 'bar', 10.5);
+    }
+}

+ 125 - 0
tests/Predis/Commands/HashIncrementByTest.php

@@ -0,0 +1,125 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashIncrementByTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashIncrementBy';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HINCRBY';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'field', 10);
+        $expected = array('key', 'field', 10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame(10, $this->getCommand()->parseResponse(10));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'field', 10);
+        $expected = array('prefix:key', 'field', 10);
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testIncrementsValueOfFieldByInteger()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame(10, $redis->hincrby('metavars', 'foo', 10));
+        $this->assertSame(5, $redis->hincrby('metavars', 'hoge', 5));
+        $this->assertSame(15, $redis->hincrby('metavars', 'hoge', 10));
+        $this->assertSame(array('foo' => '10', 'hoge' => '15'), $redis->hgetall('metavars'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testDecrementsValueOfFieldByInteger()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame(-10, $redis->hincrby('metavars', 'foo', -10));
+        $this->assertSame(-5, $redis->hincrby('metavars', 'hoge', -5));
+        $this->assertSame(-15, $redis->hincrby('metavars', 'hoge', -10));
+        $this->assertSame(array('foo' => '-10', 'hoge' => '-15'), $redis->hgetall('metavars'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR hash value is not an integer
+     */
+    public function testThrowsExceptionOnStringField()
+    {
+        $redis = $this->getClient();
+
+        $redis->hset('metavars', 'foo', 'bar');
+        $redis->hincrby('metavars', 'foo', 10);
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hincrby('foo', 'bar', 10);
+    }
+}

+ 104 - 0
tests/Predis/Commands/HashKeysTest.php

@@ -0,0 +1,104 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashKeysTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashKeys';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HKEYS';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = array('foo', 'hoge', 'lol');
+        $expected = array('foo', 'hoge', 'lol');
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key');
+        $expected = array('prefix:key');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsKeysOfHash()
+    {
+        $redis = $this->getClient();
+
+        $redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo', 'lol', 'wut');
+
+        $this->assertSame(array('foo', 'hoge', 'lol'), $redis->hkeys('metavars'));
+        $this->assertSame(array(), $redis->hkeys('unknown'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hkeys('foo');
+    }
+}

+ 99 - 0
tests/Predis/Commands/HashLengthTest.php

@@ -0,0 +1,99 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashLengthTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashLength';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HLEN';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame(1, $this->getCommand()->parseResponse(1));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key');
+        $expected = array('prefix:key');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsLengthOfHash()
+    {
+        $redis = $this->getClient();
+
+        $redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo', 'lol', 'wut');
+
+        $this->assertSame(3, $redis->hlen('metavars'));
+        $this->assertSame(0, $redis->hlen('unknown'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hlen('foo');
+    }
+}

+ 126 - 0
tests/Predis/Commands/HashSetMultipleTest.php

@@ -0,0 +1,126 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashSetMultipleTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashSetMultiple';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HMSET';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'field1', 'value1', 'field2', 'value2');
+        $expected = array('key', 'field1', 'value1', 'field2', 'value2');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArgumentsFieldsValuesAsSingleArray()
+    {
+        $arguments = array('key', array('field1' => 'value1', 'field2' => 'value2'));
+        $expected = array('key', 'field1', 'value1', 'field2', 'value2');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertTrue($this->getCommand()->parseResponse(true));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'field1', 'value1', 'field2', 'value2');
+        $expected = array('prefix:key', 'field1', 'value1', 'field2', 'value2');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSetsSpecifiedFieldsOfHash()
+    {
+        $redis = $this->getClient();
+
+        $this->assertTrue($redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo'));
+        $this->assertSame(array('foo' => 'bar', 'hoge' => 'piyo'), $redis->hgetall('metavars'));
+
+        $this->assertTrue($redis->hmset('metavars', 'foo', 'barbar', 'lol', 'wut'));
+        $this->assertSame(array('foo' => 'barbar', 'hoge' => 'piyo', 'lol' => 'wut'), $redis->hgetall('metavars'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSetsTheSpecifiedFie()
+    {
+        $redis = $this->getClient();
+
+        $redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo', 'lol', 'wut');
+
+        $this->assertSame(array('foo' => 'bar', 'hoge' => 'piyo', 'lol' => 'wut'), $redis->hgetall('metavars'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('metavars', 'bar');
+        $redis->hmset('metavars', 'foo', 'bar');
+    }
+}

+ 103 - 0
tests/Predis/Commands/HashSetPreserveTest.php

@@ -0,0 +1,103 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashSetPreserveTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashSetPreserve';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HSETNX';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'field', 'value');
+        $expected = array('key', 'field', 'value');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(1));
+        $this->assertFalse($command->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'field', 'value');
+        $expected = array('prefix:key', 'field', 'value');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSetsNewFieldsAndPreserversExistingOnes()
+    {
+        $redis = $this->getClient();
+
+        $this->assertTrue($redis->hsetnx('metavars', 'foo', 'bar'));
+        $this->assertTrue($redis->hsetnx('metavars', 'hoge', 'piyo'));
+        $this->assertFalse($redis->hsetnx('metavars', 'foo', 'barbar'));
+
+        $this->assertSame(array('bar', 'piyo'), $redis->hmget('metavars', 'foo', 'hoge'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('metavars', 'foo');
+        $redis->hsetnx('metavars', 'foo', 'bar');
+    }
+}

+ 102 - 0
tests/Predis/Commands/HashSetTest.php

@@ -0,0 +1,102 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashSetTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashSet';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HSET';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'field', 'value');
+        $expected = array('key', 'field', 'value');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(1));
+        $this->assertFalse($command->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'field', 'value');
+        $expected = array('prefix:key', 'field', 'value');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSetsValueOfSpecifiedField()
+    {
+        $redis = $this->getClient();
+
+        $this->assertTrue($redis->hset('metavars', 'foo', 'bar'));
+        $this->assertTrue($redis->hset('metavars', 'hoge', 'piyo'));
+
+        $this->assertSame(array('bar', 'piyo'), $redis->hmget('metavars', 'foo', 'hoge'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('metavars', 'foo');
+        $redis->hset('metavars', 'foo', 'bar');
+    }
+}

+ 104 - 0
tests/Predis/Commands/HashValuesTest.php

@@ -0,0 +1,104 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-hash
+ */
+class HashValuesTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\HashValues';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'HVALS';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = array('foo', 'hoge', 'lol');
+        $expected = array('foo', 'hoge', 'lol');
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key');
+        $expected = array('prefix:key');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsValuesOfHash()
+    {
+        $redis = $this->getClient();
+
+        $redis->hmset('metavars', 'foo', 'bar', 'hoge', 'piyo', 'lol', 'wut');
+
+        $this->assertSame(array('bar', 'piyo', 'wut'), $redis->hvals('metavars'));
+        $this->assertSame(array(), $redis->hvals('unknown'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->hvals('foo');
+    }
+}

+ 108 - 0
tests/Predis/Commands/KeyDeleteTest.php

@@ -0,0 +1,108 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyDeleteTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyDelete';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'DEL';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key1', 'key2', 'key3');
+        $expected = array('key1', 'key2', 'key3');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArgumentsAsSingleArray()
+    {
+        $arguments = array(array('key1', 'key2', 'key3'));
+        $expected = array('key1', 'key2', 'key3');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertSame(10, $command->parseResponse(10));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key1', 'key2', 'key3');
+        $expected = array('prefix:key1', 'prefix:key2', 'prefix:key3');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsNumberOfDeletedKeys()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame(0, $redis->del('foo'));
+
+        $redis->set('foo', 'bar');
+        $this->assertSame(1, $redis->del('foo'));
+
+        $redis->set('foo', 'bar');
+        $this->assertSame(1, $redis->del('foo', 'hoge'));
+
+        $redis->set('foo', 'bar');
+        $redis->set('hoge', 'piyo');
+        $this->assertSame(2, $redis->del('foo', 'hoge'));
+    }
+}

+ 97 - 0
tests/Predis/Commands/KeyExistsTest.php

@@ -0,0 +1,97 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyExistsTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyExists';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'EXISTS';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(1));
+        $this->assertFalse($command->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key');
+        $expected = array('prefix:key');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsTrueIfKeyExists()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $this->assertTrue($redis->exists('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsFalseIfKeyDoesNotExist()
+    {
+        $redis = $this->getClient();
+
+        $this->assertFalse($redis->exists('foo'));
+    }
+}

+ 119 - 0
tests/Predis/Commands/KeyExpireAtTest.php

@@ -0,0 +1,119 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyExpireAtTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyExpireAt';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'EXPIREAT';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'ttl');
+        $expected = array('key', 'ttl');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(1));
+        $this->assertFalse($command->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'value');
+        $expected = array('prefix:key', 'value');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsFalseOnNonExistingKeys()
+    {
+        $redis = $this->getClient();
+
+        $this->assertFalse($redis->expireat('foo', 2));
+    }
+
+    /**
+     * @group connected
+     * @group slow
+     */
+    public function testCanExpireKeys()
+    {
+        $redis = $this->getClient();
+
+        $now = time();
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->expireat('foo', $now + 1));
+        $this->assertLessThanOrEqual(1, $redis->ttl('foo'));
+
+        $this->sleep(2);
+
+        $this->assertFalse($redis->exists('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testDeletesKeysOnPastUnixTime()
+    {
+        $redis = $this->getClient();
+
+        $now = time();
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->expireat('foo', $now - 100));
+        $this->assertFalse($redis->exists('foo'));
+    }
+}

+ 132 - 0
tests/Predis/Commands/KeyExpireTest.php

@@ -0,0 +1,132 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyExpireTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyExpire';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'EXPIRE';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'ttl');
+        $expected = array('key', 'ttl');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(1));
+        $this->assertFalse($command->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'value');
+        $expected = array('prefix:key', 'value');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsFalseOnNonExistingKeys()
+    {
+        $redis = $this->getClient();
+
+        $this->assertFalse($redis->expire('foo', 2));
+    }
+
+    /**
+     * @group connected
+     * @group slow
+     */
+    public function testCanExpireKeys()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->expire('foo', 1));
+        $this->assertSame(1, $redis->ttl('foo'));
+
+        $this->sleep(2.0);
+
+        $this->assertFalse($redis->exists('foo'));
+    }
+
+    /**
+     * @group connected
+     * @group slow
+     */
+    public function testConsistencyWithTTL()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->expire('foo', 10));
+        $this->sleep(1.5);
+        $this->assertLessThan(10, $redis->ttl('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testDeletesKeysOnNegativeTTL()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->expire('foo', -10));
+        $this->assertFalse($redis->exists('foo'));
+    }
+}

+ 94 - 0
tests/Predis/Commands/KeyKeysTest.php

@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyKeysTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyKeys';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'KEYS';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('pattern:*');
+        $expected = array('pattern:*');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = array('key1', 'key2', 'key3');
+        $parsed = array('key1', 'key2', 'key3');
+
+        $this->assertSame($parsed, $this->getCommand()->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('pattern');
+        $expected = array('prefix:pattern');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsArrayOfMatchingKeys()
+    {
+        $keys = array('aaa' => 1, 'aba' => 2, 'aca' => 3);
+        $keysNS = array('metavar:foo' => 'bar', 'metavar:hoge' => 'piyo');
+        $keysAll = array_merge($keys, $keysNS);
+
+        $redis = $this->getClient();
+        $redis->mset($keysAll);
+
+        $this->assertSame(array(), $redis->keys('nomatch:*'));
+        $this->assertSameValues(array_keys($keysNS), $redis->keys('metavar:*'));
+        $this->assertSameValues(array_keys($keysAll), $redis->keys('*'));
+        $this->assertSameValues(array_keys($keys), $redis->keys('a?a'));
+    }
+}

+ 80 - 0
tests/Predis/Commands/KeyKeysV12xTest.php

@@ -0,0 +1,80 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * We only perform disconnected tests for this commands because
+ * it is too old (Redis v1.2) and expects a different response
+ * format.
+ *
+ * @group commands
+ * @group realm-key
+ */
+class KeyKeysV12xTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyKeysV12x';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'KEYS';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('pattern:*');
+        $expected = array('pattern:*');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = 'key1 key2 key3';
+        $parsed = array('key1', 'key2', 'key3');
+
+        $this->assertSame($parsed, $this->getCommand()->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('pattern');
+        $expected = array('prefix:pattern');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+}

+ 110 - 0
tests/Predis/Commands/KeyMoveTest.php

@@ -0,0 +1,110 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyMoveTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyMove';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'MOVE';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 10);
+        $expected = array('key', 10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(1));
+        $this->assertFalse($command->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'db');
+        $expected = array('prefix:key', 'db');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     * @todo This test fails if REDIS_SERVER_DBNUM is 0.
+     */
+    public function testMovesKeysToDifferentDatabases()
+    {
+        $db = REDIS_SERVER_DBNUM - 1;
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->move('foo', $db));
+        $this->assertFalse($redis->exists('foo'));
+
+        $redis->select($db);
+        $this->assertTrue($redis->exists('foo'));
+
+        $redis->del('foo');
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR index out of range
+     */
+    public function testThrowsExceptionOnInvalidDatabases()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $redis->move('foo', 100000000);
+    }
+}

+ 112 - 0
tests/Predis/Commands/KeyPersistTest.php

@@ -0,0 +1,112 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyPersistTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyPersist';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'PERSIST';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(1));
+        $this->assertFalse($command->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key');
+        $expected = array('prefix:key');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testRemovesExpireFromKey()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->expire('foo', 10);
+
+        $this->assertTrue($redis->persist('foo'));
+        $this->assertSame(-1, $redis->ttl('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsFalseOnNonExpiringKeys()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertFalse($redis->persist('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsFalseOnNonExistentKeys()
+    {
+        $redis = $this->getClient();
+
+        $this->assertFalse($redis->persist('foo'));
+    }
+}

+ 109 - 0
tests/Predis/Commands/KeyPreciseExpireAtTest.php

@@ -0,0 +1,109 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyPreciseExpireAtTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyPreciseExpireAt';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'PEXPIREAT';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 100);
+        $expected = array('key', 100);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(1));
+        $this->assertFalse($command->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'value');
+        $expected = array('prefix:key', 'value');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     * @group slow
+     */
+    public function testCanExpireKeys()
+    {
+        $ttl = 1.5;
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->pexpireat('foo', time() + $ttl * 1000));
+        $this->assertLessThan($ttl * 1000, $redis->pttl('foo'));
+
+        $this->sleep($ttl + 0.5);
+
+        $this->assertFalse($redis->exists('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testDeletesKeysOnPastUnixTime()
+    {
+        $redis = $this->getClient();
+
+        $now = time();
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->expireat('foo', time() - 100000));
+        $this->assertFalse($redis->exists('foo'));
+    }
+}

+ 134 - 0
tests/Predis/Commands/KeyPreciseExpireTest.php

@@ -0,0 +1,134 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyPreciseExpireTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyPreciseExpire';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'PEXPIRE';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 100);
+        $expected = array('key', 100);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertTrue($command->parseResponse(1));
+        $this->assertFalse($command->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'value');
+        $expected = array('prefix:key', 'value');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsFalseOnNonExistingKeys()
+    {
+        $redis = $this->getClient();
+
+        $this->assertFalse($redis->pexpire('foo', 20000));
+    }
+
+    /**
+     * @group connected
+     * @group slow
+     */
+    public function testCanExpireKeys()
+    {
+        $ttl = 1 * 1000;
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->pexpire('foo', $ttl));
+
+        $this->sleep(1.2);
+        $this->assertFalse($redis->exists('foo'));
+    }
+
+    /**
+     * @group connected
+     * @group slow
+     */
+    public function testConsistencyWithTTL()
+    {
+        $ttl = 1 * 1000;
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->pexpire('foo', $ttl));
+
+        $this->sleep(0.5);
+        $this->assertLessThanOrEqual($ttl, $redis->pttl('foo'));
+        $this->assertGreaterThan($ttl - 800, $redis->pttl('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testDeletesKeysOnNegativeTTL()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->pexpire('foo', -10000));
+        $this->assertFalse($redis->exists('foo'));
+    }
+}

+ 109 - 0
tests/Predis/Commands/KeyPreciseTimeToLiveTest.php

@@ -0,0 +1,109 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyPreciseTimeToLiveTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyPreciseTimeToLive';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'PTTL';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 10);
+        $expected = array('key', 10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertSame(100, $command->parseResponse(100));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 10);
+        $expected = array('prefix:key', 10);
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsTTL()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->expire('foo', 10);
+
+        $this->assertLessThanOrEqual(10000, $redis->pttl('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsLessThanZeroOnNonExistingKeys()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame(-1, $redis->pttl('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsLessThanZeroOnNonExpiringKeys()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $this->assertSame(-1, $redis->pttl('foo'));
+    }
+}

+ 87 - 0
tests/Predis/Commands/KeyRandomTest.php

@@ -0,0 +1,87 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyRandomTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyRandom';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'RANDOMKEY';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array();
+        $expected = array();
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = 'key';
+        $expected = 'key';
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsFalseOnNonExpiringKeys()
+    {
+        $keys = array('key:1' => 1, 'key:2' => 2, 'key:3' => 3);
+
+        $redis = $this->getClient();
+        $redis->mset($keys);
+
+        $this->assertContains($redis->randomkey(), array_keys($keys));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsNullOnEmptyDatabase()
+    {
+        $redis = $this->getClient();
+
+        $this->assertNull($redis->randomkey());
+    }
+}

+ 100 - 0
tests/Predis/Commands/KeyRenamePreserveTest.php

@@ -0,0 +1,100 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyRenamePreserveTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyRenamePreserve';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'RENAMENX';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'newkey');
+        $expected = array('key', 'newkey');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertTrue($this->getCommand()->parseResponse(1));
+        $this->assertFalse($this->getCommand()->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'newkey');
+        $expected = array('prefix:key', 'prefix:newkey');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testRenamesKeys()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->renamenx('foo', 'foofoo'));
+        $this->assertFalse($redis->exists('foo'));
+        $this->assertTrue($redis->exists('foofoo'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR no such key
+     */
+    public function testReturnsFalseOnNonExistingKeys()
+    {
+        $redis = $this->getClient();
+
+        $this->assertFalse($redis->renamenx('foo', 'foobar'));
+    }
+}

+ 99 - 0
tests/Predis/Commands/KeyRenameTest.php

@@ -0,0 +1,99 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyRenameTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyRename';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'RENAME';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'newkey');
+        $expected = array('key', 'newkey');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertTrue($this->getCommand()->parseResponse(true));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'newkey');
+        $expected = array('prefix:key', 'prefix:newkey');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testRenamesKeys()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+
+        $this->assertTrue($redis->rename('foo', 'foofoo'));
+        $this->assertFalse($redis->exists('foo'));
+        $this->assertTrue($redis->exists('foofoo'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR no such key
+     */
+    public function testThrowsExceptionOnNonExistingKeys()
+    {
+        $redis = $this->getClient();
+
+        $redis->rename('foo', 'foobar');
+    }
+}

+ 270 - 0
tests/Predis/Commands/KeySortTest.php

@@ -0,0 +1,270 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeySortTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeySort';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'SORT';
+    }
+
+    /**
+     * Utility method to to an LPUSH of some unordered values on a key.
+     *
+     * @param Predis\Client $redis Redis client instance.
+     * @param string $key Target key
+     * @return array
+     */
+    protected function lpushUnorderedList(Predis\Client $redis, $key)
+    {
+        $list = array(2, 100, 3, 1, 30, 10);
+        $redis->lpush($key, $list);
+
+        return $list;
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $modifiers = array(
+            'by' => 'by_key_*',
+            'limit' => array(1, 4),
+            'get' => array('object_*', '#'),
+            'sort'  => 'asc',
+            'alpha' => true,
+            'store' => 'destination_key',
+        );
+        $arguments = array('key', $modifiers);
+
+        $expected = array(
+            'key', 'BY', 'by_key_*', 'GET', 'object_*', 'GET', '#',
+            'LIMIT', 1, 4, 'ASC', 'ALPHA', 'STORE', 'destination_key'
+        );
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertEquals($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testGetModifierCanBeString()
+    {
+        $arguments = array('key', array('get' => '#'));
+        $expected = array('key', 'GET', '#');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = array('value1', 'value2', 'value3');
+        $expected = array('value1', 'value2', 'value3');
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $modifiers = array(
+            'by' => 'by_key_*',
+            'limit' => array(1, 4),
+            'get' => array('object_*', '#'),
+            'sort'  => 'asc',
+            'alpha' => true,
+            'store' => 'destination_key',
+        );
+        $arguments = array('key', $modifiers);
+
+        $expected = array(
+            'prefix:key', 'BY', 'prefix:by_key_*', 'GET', 'prefix:object_*', 'GET', '#',
+            'LIMIT', 1, 4, 'ASC', 'ALPHA', 'STORE', 'prefix:destination_key'
+        );
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testBasicSort()
+    {
+        $redis = $this->getClient();
+        $redis->lpush('list:unordered', $unordered = array(2, 100, 3, 1, 30, 10));
+
+        $this->assertEquals(array(1, 2, 3, 10, 30, 100), $redis->sort('list:unordered'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSortWithAscOrDescModifier()
+    {
+        $redis = $this->getClient();
+        $redis->lpush('list:unordered', $unordered = array(2, 100, 3, 1, 30, 10));
+
+        $this->assertEquals(
+            array(100, 30, 10, 3, 2, 1),
+            $redis->sort('list:unordered', array(
+                'sort' => 'desc',
+            ))
+        );
+
+        $this->assertEquals(
+            array(1, 2, 3, 10, 30, 100),
+            $redis->sort('list:unordered', array(
+                'sort' => 'asc',
+            ))
+        );
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSortWithLimitModifier()
+    {
+        $redis = $this->getClient();
+        $redis->lpush('list:unordered', $unordered = array(2, 100, 3, 1, 30, 10));
+
+        $this->assertEquals(
+            array(1, 2, 3),
+            $redis->sort('list:unordered', array(
+                'limit' => array(0, 3),
+            ))
+        );
+
+        $this->assertEquals(
+            array(10, 30),
+            $redis->sort('list:unordered', array(
+                'limit' => array(3, 2)
+            ))
+        );
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSortWithAlphaModifier()
+    {
+        $redis = $this->getClient();
+        $redis->lpush('list:unordered', $unordered = array(2, 100, 3, 1, 30, 10));
+
+        $this->assertEquals(
+            array(1, 10, 100, 2, 3, 30),
+            $redis->sort('list:unordered', array(
+                'alpha' => true
+            ))
+        );
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSortWithStoreModifier()
+    {
+        $redis = $this->getClient();
+        $redis->lpush('list:unordered', $unordered = array(2, 100, 3, 1, 30, 10));
+
+        $this->assertEquals(
+            count($unordered),
+            $redis->sort('list:unordered', array(
+                'store' => 'list:ordered'
+            ))
+        );
+
+        $this->assertEquals(array(1, 2, 3, 10, 30, 100),  $redis->lrange('list:ordered', 0, -1));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSortWithCombinedModifiers()
+    {
+        $redis = $this->getClient();
+        $redis->lpush('list:unordered', $unordered = array(2, 100, 3, 1, 30, 10));
+
+        $this->assertEquals(
+            array(30, 10, 3, 2),
+            $redis->sort('list:unordered', array(
+                'alpha' => false,
+                'sort'  => 'desc',
+                'limit' => array(1, 4)
+            ))
+        );
+    }
+
+    /**
+     * @group connected
+     */
+    public function testSortWithGetModifiers()
+    {
+        $redis = $this->getClient();
+        $redis->lpush('list:unordered', $unordered = array(2, 100, 3, 1, 30, 10));
+
+        $redis->rpush('list:uids', $uids = array(1003, 1001, 1002, 1000));
+        $redis->mset($sortget = array(
+            'uid:1000' => 'foo',  'uid:1001' => 'bar',
+            'uid:1002' => 'hoge', 'uid:1003' => 'piyo',
+        ));
+
+        $this->assertEquals(array_values($sortget), $redis->sort('list:uids', array('get' => 'uid:*')));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->sort('foo');
+    }
+}

+ 109 - 0
tests/Predis/Commands/KeyTimeToLiveTest.php

@@ -0,0 +1,109 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyTimeToLiveTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyTimeToLive';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'TTL';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 10);
+        $expected = array('key', 10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $command = $this->getCommand();
+
+        $this->assertSame(100, $command->parseResponse(100));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 10);
+        $expected = array('prefix:key', 10);
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsTTL()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->expire('foo', 10);
+
+        $this->assertSame(10, $redis->ttl('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsLessThanZeroOnNonExistingKeys()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame(-1, $redis->ttl('foo'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsLessThanZeroOnNonExpiringKeys()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $this->assertSame(-1, $redis->ttl('foo'));
+    }
+}

+ 98 - 0
tests/Predis/Commands/KeyTypeTest.php

@@ -0,0 +1,98 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-key
+ */
+class KeyTypeTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\KeyType';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'TYPE';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame('type', $this->getCommand()->parseResponse('type'));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key');
+        $expected = array('prefix:key');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsTypeOfKey()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame('none', $redis->type('type:keydoesnotexist'));
+
+        $redis->set('type:string', 'foobar');
+        $this->assertSame('string', $redis->type('type:string'));
+
+        $redis->lpush('type:list', 'foobar');
+        $this->assertSame('list', $redis->type('type:list'));
+
+        $redis->sadd('type:set', 'foobar');
+        $this->assertSame('set', $redis->type('type:set'));
+
+        $redis->zadd('type:zset', 0, 'foobar');
+        $this->assertSame('zset', $redis->type('type:zset'));
+
+        $redis->hset('type:hash', 'foo', 'bar');
+        $this->assertSame('hash', $redis->type('type:hash'));
+    }
+}

+ 115 - 0
tests/Predis/Commands/ListIndexTest.php

@@ -0,0 +1,115 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-list
+ */
+class ListIndexTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ListIndex';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'LINDEX';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 1);
+        $expected = array('key', 1);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame(0, $this->getCommand()->parseResponse(0));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 1);
+        $expected = array('prefix:key', 1);
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsElementAtIndex()
+    {
+        $redis = $this->getClient();
+
+        $redis->rpush('letters', 'a', 'b', 'c', 'd', 'e');
+
+        $this->assertSame('a', $redis->lindex('letters', 0));
+        $this->assertSame('c', $redis->lindex('letters', 2));
+        $this->assertNull($redis->lindex('letters', 100));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsElementAtNegativeIndex()
+    {
+        $redis = $this->getClient();
+
+        $redis->rpush('letters', 'a', 'b', 'c', 'd', 'e');
+
+        $this->assertSame('a', $redis->lindex('letters', -0));
+        $this->assertSame('c', $redis->lindex('letters', -3));
+        $this->assertSame('e', $redis->lindex('letters', -1));
+        $this->assertNull($redis->lindex('letters', -100));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->lindex('foo', 0);
+    }
+}

+ 123 - 0
tests/Predis/Commands/ListInsertTest.php

@@ -0,0 +1,123 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-list
+ */
+class ListInsertTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ListInsert';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'LINSERT';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key', 'before', 'value1', 'value2');
+        $expected = array('key', 'before', 'value1', 'value2');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame(1, $this->getCommand()->parseResponse(1));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key', 'before', 'value1', 'value2');
+        $expected = array('prefix:key', 'before', 'value1', 'value2');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsLengthOfListAfterInser()
+    {
+        $redis = $this->getClient();
+
+        $redis->rpush('letters', 'a', 'c', 'e');
+
+        $this->assertSame(4, $redis->linsert('letters', 'before', 'c', 'b'));
+        $this->assertSame(5, $redis->linsert('letters', 'after', 'c', 'd'));
+        $this->assertSame(array('a', 'b', 'c', 'd', 'e'), $redis->lrange('letters', 0, -1));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsNegativeLengthOnFailedInsert()
+    {
+        $redis = $this->getClient();
+
+        $redis->rpush('letters', 'a', 'c', 'e');
+
+        $this->assertSame(-1, $redis->linsert('letters', 'before', 'n', 'm'));
+        $this->assertSame(-1, $redis->linsert('letters', 'after', 'o', 'p'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsZeroLengthOnNonExistingList()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame(0, $redis->linsert('letters', 'after', 'a', 'b'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->linsert('foo', 'BEFORE', 'bar', 'baz');
+    }
+}

+ 110 - 0
tests/Predis/Commands/ListLengthTest.php

@@ -0,0 +1,110 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-list
+ */
+class ListLengthTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ListLength';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'LLEN';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame(1, $this->getCommand()->parseResponse(1));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key');
+        $expected = array('prefix:key');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsLengthOfList()
+    {
+        $redis = $this->getClient();
+
+        $redis->rpush('letters', 'a', 'b', 'c');
+        $this->assertSame(3, $redis->llen('letters'));
+
+        $redis->rpush('letters', 'd', 'e', 'f');
+        $this->assertSame(6, $redis->llen('letters'));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsZeroLengthOnNonExistingList()
+    {
+        $redis = $this->getClient();
+
+        $this->assertSame(0, $redis->llen('letters'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->llen('foo');
+    }
+}

+ 94 - 0
tests/Predis/Commands/ListPopFirstBlockingTest.php

@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-list
+ * @todo Testing blocking pop operations against Redis using PHP is
+ *       tricky, so we will skip these kind of tests for now.
+ */
+class ListPopFirstBlockingTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ListPopFirstBlocking';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'BLPOP';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key1', 'key2', 'key3', 10);
+        $expected = array('key1', 'key2', 'key3', 10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArgumentsKeysAsSingleArray()
+    {
+        $arguments = array(array('key1', 'key2', 'key3'), 10);
+        $expected = array('key1', 'key2', 'key3', 10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = array('key', 'value');
+        $expected = array('key', 'value');
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key1', 'key2', 'key3', 10);
+        $expected = array('prefix:key1', 'prefix:key2', 'prefix:key3', 10);
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+}

+ 110 - 0
tests/Predis/Commands/ListPopFirstTest.php

@@ -0,0 +1,110 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-list
+ */
+class ListPopFirstTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ListPopFirst';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'LPOP';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key');
+        $expected = array('key');
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame('element', $this->getCommand()->parseResponse('element'));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key');
+        $expected = array('prefix:key');
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group connected
+     */
+    public function testPopsTheFirstElementFromList()
+    {
+        $redis = $this->getClient();
+
+        $redis->rpush('letters', 'a', 'b', 'c', 'd');
+
+        $this->assertSame('a', $redis->lpop('letters'));
+        $this->assertSame('b', $redis->lpop('letters'));
+        $this->assertSame(array('c', 'd'), $redis->lrange('letters', 0, -1));
+    }
+
+    /**
+     * @group connected
+     */
+    public function testReturnsNullOnEmptyList()
+    {
+        $redis = $this->getClient();
+
+        $this->assertNull($redis->lpop('letters'));
+    }
+
+    /**
+     * @group connected
+     * @expectedException Predis\ServerException
+     * @expectedExceptionMessage ERR Operation against a key holding the wrong kind of value
+     */
+    public function testThrowsExceptionOnWrongType()
+    {
+        $redis = $this->getClient();
+
+        $redis->set('foo', 'bar');
+        $redis->lpop('foo');
+    }
+}

+ 94 - 0
tests/Predis/Commands/ListPopLastBlockingTest.php

@@ -0,0 +1,94 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-list
+ * @todo Testing blocking pop operations against Redis using PHP is
+ *       tricky, so we will skip these kind of tests for now.
+ */
+class ListPopLastBlockingTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ListPopLastBlocking';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'BRPOP';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key1', 'key2', 'key3', 10);
+        $expected = array('key1', 'key2', 'key3', 10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArgumentsKeysAsSingleArray()
+    {
+        $arguments = array(array('key1', 'key2', 'key3'), 10);
+        $expected = array('key1', 'key2', 'key3', 10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $raw = array('key', 'value');
+        $expected = array('key', 'value');
+
+        $command = $this->getCommand();
+
+        $this->assertSame($expected, $command->parseResponse($raw));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key1', 'key2', 'key3', 10);
+        $expected = array('prefix:key1', 'prefix:key2', 'prefix:key3', 10);
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+}

+ 75 - 0
tests/Predis/Commands/ListPopLastPushHeadBlockingTest.php

@@ -0,0 +1,75 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Commands;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+/**
+ * @group commands
+ * @group realm-list
+ * @todo Testing blocking pop operations against Redis using PHP is
+ *       tricky, so we will skip these kind of tests for now.
+ */
+class ListPopLastPushHeadBlockingTest extends CommandTestCase
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedCommand()
+    {
+        return 'Predis\Commands\ListPopLastPushHeadBlocking';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getExpectedId()
+    {
+        return 'BRPOPLPUSH';
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testFilterArguments()
+    {
+        $arguments = array('key:source', 'key:destination', 10);
+        $expected = array('key:source', 'key:destination', 10);
+
+        $command = $this->getCommand();
+        $command->setArguments($arguments);
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testParseResponse()
+    {
+        $this->assertSame('element', $this->getCommand()->parseResponse('element'));
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testPrefixKeys()
+    {
+        $arguments = array('key:source', 'key:destination', 10);
+        $expected = array('prefix:key:source', 'prefix:key:destination', 10);
+
+        $command = $this->getCommandWithArgumentsArray($arguments);
+        $command->prefixKeys('prefix:');
+
+        $this->assertSame($expected, $command->getArguments());
+    }
+}

Some files were not shown because too many files changed in this diff