Browse Source

[ŧests] Add tests for new SCAN, SSCAN, ZSCAN and HSCAN iterators.

Right now we don't have integration tests since the returned values from
Redis may not always be predictable, but the current tests should suffice.
Daniele Alessandri 11 years ago
parent
commit
b7f411a252

+ 425 - 0
tests/Predis/Iterator/Scan/HashIteratorTest.php

@@ -0,0 +1,425 @@
+<?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\Iterator\Scan;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+use Predis\Client;
+use Predis\Profile\ServerProfile;
+
+/**
+ * @group realm-iterators
+ */
+class HashIteratorTest extends StandardTestCase
+{
+    /**
+     * @group disconnected
+     * @expectedException Predis\NotSupportedException
+     * @expectedExceptionMessage The specified server profile does not support the `HSCAN` command.
+     */
+    public function testThrowsExceptionOnInvalidServerProfile()
+    {
+        $client = $this->getMock('Predis\ClientInterface');
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.0')));
+
+        $iterator = new HashIterator($client, 'key:hash');
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationOnSingleFetch()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->once())
+               ->method('hscan')
+               ->with('key:hash', 0, array())
+               ->will($this->returnValue(array(0, array(
+                    'field:1st' => 'value:1st', 'field:2nd' => 'value:2nd', 'field:3rd' => 'value:3rd',
+               ))));
+
+        $iterator = new HashIterator($client, 'key:hash');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:3rd', $iterator->key());
+        $this->assertSame('value:3rd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('hscan')
+               ->with('key:hash', 0, array())
+               ->will($this->returnValue(array(2, array(
+                    'field:1st' => 'value:1st', 'field:2nd' => 'value:2nd',
+               ))));
+        $client->expects($this->at(2))
+               ->method('hscan')
+               ->with('key:hash', 2, array())
+               ->will($this->returnValue(array(0, array(
+                    'field:3rd' => 'value:3rd',
+               ))));
+
+        $iterator = new HashIterator($client, 'key:hash');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:3rd', $iterator->key());
+        $this->assertSame('value:3rd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithMultipleFetchesAndHoles()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('hscan')
+               ->with('key:hash', 0, array())
+               ->will($this->returnValue(array(2, array(
+                    'field:1st' => 'value:1st', 'field:2nd' => 'value:2nd',
+               ))));
+        $client->expects($this->at(2))
+               ->method('hscan')
+               ->with('key:hash', 2, array())
+               ->will($this->returnValue(array(5, array())));
+        $client->expects($this->at(3))
+               ->method('hscan')
+               ->with('key:hash', 5, array())
+               ->will($this->returnValue(array(0, array(
+                    'field:3rd' => 'value:3rd',
+               ))));
+
+        $iterator = new HashIterator($client, 'key:hash');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:3rd', $iterator->key());
+        $this->assertSame('value:3rd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionMatch()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('hscan')
+               ->with('key:hash', 0, array('MATCH' => 'field:*'))
+               ->will($this->returnValue(array(2, array(
+                    'field:1st' => 'value:1st', 'field:2nd' => 'value:2nd',
+               ))));
+
+        $iterator = new HashIterator($client, 'key:hash', 'field:*');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionMatchOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('hscan')
+               ->with('key:hash', 0, array('MATCH' => 'field:*'))
+               ->will($this->returnValue(array(1, array(
+                    'field:1st' => 'value:1st',
+                ))));
+        $client->expects($this->at(2))
+               ->method('hscan')
+               ->with('key:hash', 1, array('MATCH' => 'field:*'))
+               ->will($this->returnValue(array(0, array(
+                    'field:2nd' => 'value:2nd',
+                ))));
+
+        $iterator = new HashIterator($client, 'key:hash', 'field:*');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionCount()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('hscan')
+               ->with('key:hash', 0, array('COUNT' => 2))
+               ->will($this->returnValue(array(0, array(
+                    'field:1st' => 'value:1st', 'field:2nd' => 'value:2nd',
+               ))));
+
+        $iterator = new HashIterator($client, 'key:hash', null, 2);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionCountOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('hscan')
+               ->with('key:hash', 0, array('COUNT' => 1))
+               ->will($this->returnValue(array(1, array(
+                    'field:1st' => 'value:1st',
+                ))));
+        $client->expects($this->at(2))
+               ->method('hscan')
+               ->with('key:hash', 1, array('COUNT' => 1))
+               ->will($this->returnValue(array(0, array(
+                    'field:2nd' => 'value:2nd',
+                ))));
+
+        $iterator = new HashIterator($client, 'key:hash', null, 1);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionsMatchAndCount()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('hscan')
+               ->with('key:hash', 0, array('MATCH' => 'field:*', 'COUNT' => 2))
+               ->will($this->returnValue(array(0, array(
+                    'field:1st' => 'value:1st', 'field:2nd' => 'value:2nd',
+               ))));
+
+        $iterator = new HashIterator($client, 'key:hash', 'field:*', 2);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionsMatchAndCountOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('hscan')
+               ->with('key:hash', 0, array('MATCH' => 'field:*', 'COUNT' => 1))
+               ->will($this->returnValue(array(1, array(
+                    'field:1st' => 'value:1st',
+                ))));
+        $client->expects($this->at(2))
+               ->method('hscan')
+               ->with('key:hash', 1, array('MATCH' => 'field:*', 'COUNT' => 1))
+               ->will($this->returnValue(array(0, array(
+                    'field:2nd' => 'value:2nd',
+                ))));
+
+        $iterator = new HashIterator($client, 'key:hash', 'field:*', 1);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationRewindable()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'hscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->exactly(2))
+               ->method('hscan')
+               ->with('key:hash', 0, array())
+               ->will($this->returnValue(array(0, array(
+                    'field:1st' => 'value:1st', 'field:2nd' => 'value:2nd',
+               ))));
+
+        $iterator = new HashIterator($client, 'key:hash');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->rewind();
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:1st', $iterator->key());
+        $this->assertSame('value:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('field:2nd', $iterator->key());
+        $this->assertSame('value:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+}

+ 395 - 0
tests/Predis/Iterator/Scan/KeyspaceIteratorTest.php

@@ -0,0 +1,395 @@
+<?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\Iterator\Scan;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+use Predis\Client;
+use Predis\Profile\ServerProfile;
+
+/**
+ * @group realm-iterators
+ */
+class KeyspaceIteratorTest extends StandardTestCase
+{
+    /**
+     * @group disconnected
+     * @expectedException Predis\NotSupportedException
+     * @expectedExceptionMessage The specified server profile does not support the `SCAN` command.
+     */
+    public function testThrowsExceptionOnInvalidServerProfile()
+    {
+        $client = $this->getMock('Predis\ClientInterface');
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.0')));
+
+        $iterator = new KeyspaceIterator($client);
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationOnSingleFetch()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->once())
+               ->method('scan')
+               ->with(0, array())
+               ->will($this->returnValue(array(0, array('key:1st', 'key:2nd', 'key:3rd'))));
+
+        $iterator = new KeyspaceIterator($client);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(2, $iterator->key());
+        $this->assertSame('key:3rd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('scan')
+               ->with(0, array())
+               ->will($this->returnValue(array(2, array('key:1st', 'key:2nd'))));
+        $client->expects($this->at(2))
+               ->method('scan')
+               ->with(2, array())
+               ->will($this->returnValue(array(0, array('key:3rd'))));
+
+        $iterator = new KeyspaceIterator($client);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(2, $iterator->key());
+        $this->assertSame('key:3rd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithMultipleFetchesAndHoles()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('scan')
+               ->with(0, array())
+               ->will($this->returnValue(array(2, array('key:1st', 'key:2nd'))));
+        $client->expects($this->at(2))
+               ->method('scan')
+               ->with(2, array())
+               ->will($this->returnValue(array(5, array())));
+        $client->expects($this->at(3))
+               ->method('scan')
+               ->with(5, array())
+               ->will($this->returnValue(array(0, array('key:3rd'))));
+
+        $iterator = new KeyspaceIterator($client);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(2, $iterator->key());
+        $this->assertSame('key:3rd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionMatch()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('scan')
+               ->with(0, array('MATCH' => 'key:*'))
+               ->will($this->returnValue(array(0, array('key:1st', 'key:2nd'))));
+
+        $iterator = new KeyspaceIterator($client, 'key:*');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionMatchOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('scan')
+               ->with(0, array('MATCH' => 'key:*'))
+               ->will($this->returnValue(array(1, array('key:1st'))));
+        $client->expects($this->at(2))
+               ->method('scan')
+               ->with(1, array('MATCH' => 'key:*'))
+               ->will($this->returnValue(array(0, array('key:2nd'))));
+
+        $iterator = new KeyspaceIterator($client, 'key:*');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionCount()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('scan')
+               ->with(0, array('COUNT' => 2))
+               ->will($this->returnValue(array(0, array('key:1st', 'key:2nd'))));
+
+        $iterator = new KeyspaceIterator($client, null, 2);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionCountOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('scan')
+               ->with(0, array('COUNT' => 1))
+               ->will($this->returnValue(array(1, array('key:1st'))));
+        $client->expects($this->at(2))
+               ->method('scan')
+               ->with(1, array('COUNT' => 1))
+               ->will($this->returnValue(array(0, array('key:2nd'))));
+
+        $iterator = new KeyspaceIterator($client, null, 1);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionsMatchAndCount()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('scan')
+               ->with(0, array('MATCH' => 'key:*', 'COUNT' => 2))
+               ->will($this->returnValue(array(0, array('key:1st', 'key:2nd'))));
+
+        $iterator = new KeyspaceIterator($client, 'key:*', 2);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionsMatchAndCountOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('scan')
+               ->with(0, array('MATCH' => 'key:*', 'COUNT' => 1))
+               ->will($this->returnValue(array(1, array('key:1st'))));
+        $client->expects($this->at(2))
+               ->method('scan')
+               ->with(1, array('MATCH' => 'key:*', 'COUNT' => 1))
+               ->will($this->returnValue(array(0, array('key:2nd'))));
+
+        $iterator = new KeyspaceIterator($client, 'key:*', 1);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationRewindable()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'scan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->exactly(2))
+               ->method('scan')
+               ->with(0, array())
+               ->will($this->returnValue(array(0, array('key:1st', 'key:2nd'))));
+
+        $iterator = new KeyspaceIterator($client);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->rewind();
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('key:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('key:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+}

+ 395 - 0
tests/Predis/Iterator/Scan/SetIteratorTest.php

@@ -0,0 +1,395 @@
+<?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\Iterator\Scan;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+use Predis\Client;
+use Predis\Profile\ServerProfile;
+
+/**
+ * @group realm-iterators
+ */
+class SetIteratorTest extends StandardTestCase
+{
+    /**
+     * @group disconnected
+     * @expectedException Predis\NotSupportedException
+     * @expectedExceptionMessage The specified server profile does not support the `SSCAN` command.
+     */
+    public function testThrowsExceptionOnInvalidServerProfile()
+    {
+        $client = $this->getMock('Predis\ClientInterface');
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.0')));
+
+        $iterator = new SetIterator($client, 'key:set');
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationOnSingleFetch()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->once())
+               ->method('sscan')
+               ->with('key:set', 0, array())
+               ->will($this->returnValue(array(0, array('member:1st', 'member:2nd', 'member:3rd'))));
+
+        $iterator = new SetIterator($client, 'key:set');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(2, $iterator->key());
+        $this->assertSame('member:3rd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('sscan')
+               ->with('key:set', 0, array())
+               ->will($this->returnValue(array(2, array('member:1st', 'member:2nd'))));
+        $client->expects($this->at(2))
+               ->method('sscan')
+               ->with('key:set', 2, array())
+               ->will($this->returnValue(array(0, array('member:3rd'))));
+
+        $iterator = new SetIterator($client, 'key:set');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(2, $iterator->key());
+        $this->assertSame('member:3rd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithMultipleFetchesAndHoles()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('sscan')
+               ->with('key:set', 0, array())
+               ->will($this->returnValue(array(2, array('member:1st', 'member:2nd'))));
+        $client->expects($this->at(2))
+               ->method('sscan')
+               ->with('key:set', 2, array())
+               ->will($this->returnValue(array(5, array())));
+        $client->expects($this->at(3))
+               ->method('sscan')
+               ->with('key:set', 5, array())
+               ->will($this->returnValue(array(0, array('member:3rd'))));
+
+        $iterator = new SetIterator($client, 'key:set');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(2, $iterator->key());
+        $this->assertSame('member:3rd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionMatch()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('sscan')
+               ->with('key:set', 0, array('MATCH' => 'member:*'))
+               ->will($this->returnValue(array(0, array('member:1st', 'member:2nd'))));
+
+        $iterator = new SetIterator($client, 'key:set', 'member:*');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionMatchOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('sscan')
+               ->with('key:set', 0, array('MATCH' => 'member:*'))
+               ->will($this->returnValue(array(1, array('member:1st'))));
+        $client->expects($this->at(2))
+               ->method('sscan')
+               ->with('key:set', 1, array('MATCH' => 'member:*'))
+               ->will($this->returnValue(array(0, array('member:2nd'))));
+
+        $iterator = new SetIterator($client, 'key:set', 'member:*');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionCount()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('sscan')
+               ->with('key:set', 0, array('COUNT' => 2))
+               ->will($this->returnValue(array(0, array('member:1st', 'member:2nd'))));
+
+        $iterator = new SetIterator($client, 'key:set', null, 2);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionCountOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('sscan')
+               ->with('key:set', 0, array('COUNT' => 1))
+               ->will($this->returnValue(array(1, array('member:1st'))));
+        $client->expects($this->at(2))
+               ->method('sscan')
+               ->with('key:set', 1, array('COUNT' => 1))
+               ->will($this->returnValue(array(0, array('member:2nd'))));
+
+        $iterator = new SetIterator($client, 'key:set', null, 1);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionsMatchAndCount()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('sscan')
+               ->with('key:set', 0, array('MATCH' => 'member:*', 'COUNT' => 2))
+               ->will($this->returnValue(array(0, array('member:1st', 'member:2nd'))));
+
+        $iterator = new SetIterator($client, 'key:set', 'member:*', 2);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionsMatchAndCountOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('sscan')
+               ->with('key:set', 0, array('MATCH' => 'member:*', 'COUNT' => 1))
+               ->will($this->returnValue(array(1, array('member:1st'))));
+        $client->expects($this->at(2))
+               ->method('sscan')
+               ->with('key:set', 1, array('MATCH' => 'member:*', 'COUNT' => 1))
+               ->will($this->returnValue(array(0, array('member:2nd'))));
+
+        $iterator = new SetIterator($client, 'key:set', 'member:*', 1);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationRewindable()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'sscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->exactly(2))
+               ->method('sscan')
+               ->with('key:set', 0, array())
+               ->will($this->returnValue(array(0, array('member:1st', 'member:2nd'))));
+
+        $iterator = new SetIterator($client, 'key:set');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->rewind();
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(0, $iterator->key());
+        $this->assertSame('member:1st', $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame(1, $iterator->key());
+        $this->assertSame('member:2nd', $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+}

+ 425 - 0
tests/Predis/Iterator/Scan/SortedSetIteratorTest.php

@@ -0,0 +1,425 @@
+<?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\Iterator\Scan;
+
+use \PHPUnit_Framework_TestCase as StandardTestCase;
+
+use Predis\Client;
+use Predis\Profile\ServerProfile;
+
+/**
+ * @group realm-iterators
+ */
+class SortedSetIteratorTest extends StandardTestCase
+{
+    /**
+     * @group disconnected
+     * @expectedException Predis\NotSupportedException
+     * @expectedExceptionMessage The specified server profile does not support the `ZSCAN` command.
+     */
+    public function testThrowsExceptionOnInvalidServerProfile()
+    {
+        $client = $this->getMock('Predis\ClientInterface');
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.0')));
+
+        $iterator = new SortedSetIterator($client, 'key:zset');
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationOnSingleFetch()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->once())
+               ->method('zscan')
+               ->with('key:zset', 0, array())
+               ->will($this->returnValue(array(0, array(
+                    array('member:1st', 1.0), array('member:2nd', 2.0), array('member:3rd', 3.0),
+               ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:3rd', $iterator->key());
+        $this->assertSame(3.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('zscan')
+               ->with('key:zset', 0, array())
+               ->will($this->returnValue(array(2, array(
+                    array('member:1st', 1.0), array('member:2nd', 2.0),
+               ))));
+        $client->expects($this->at(2))
+               ->method('zscan')
+               ->with('key:zset', 2, array())
+               ->will($this->returnValue(array(0, array(
+                    array('member:3rd', 3.0),
+               ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:3rd', $iterator->key());
+        $this->assertSame(3.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithMultipleFetchesAndHoles()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('zscan')
+               ->with('key:zset', 0, array())
+               ->will($this->returnValue(array(2, array(
+                    array('member:1st', 1.0), array('member:2nd', 2.0),
+               ))));
+        $client->expects($this->at(2))
+               ->method('zscan')
+               ->with('key:zset', 2, array())
+               ->will($this->returnValue(array(5, array())));
+        $client->expects($this->at(3))
+               ->method('zscan')
+               ->with('key:zset', 5, array())
+               ->will($this->returnValue(array(0, array(
+                    array('member:3rd', 3.0)
+               ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:3rd', $iterator->key());
+        $this->assertSame(3.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionMatch()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('zscan')
+               ->with('key:zset', 0, array('MATCH' => 'member:*'))
+               ->will($this->returnValue(array(2, array(
+                    array('member:1st', 1.0), array('member:2nd', 2.0),
+               ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset', 'member:*');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionMatchOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('zscan')
+               ->with('key:zset', 0, array('MATCH' => 'member:*'))
+               ->will($this->returnValue(array(1, array(
+                    array('member:1st', 1.0),
+                ))));
+        $client->expects($this->at(2))
+               ->method('zscan')
+               ->with('key:zset', 1, array('MATCH' => 'member:*'))
+               ->will($this->returnValue(array(0, array(
+                    array('member:2nd', 2.0),
+                ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset', 'member:*');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionCount()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('zscan')
+               ->with('key:zset', 0, array('COUNT' => 2))
+               ->will($this->returnValue(array(0, array(
+                    array('member:1st', 1.0), array('member:2nd', 2.0),
+               ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset', null, 2);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionCountOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('zscan')
+               ->with('key:zset', 0, array('COUNT' => 1))
+               ->will($this->returnValue(array(1, array(
+                    array('member:1st', 1.0),
+                ))));
+        $client->expects($this->at(2))
+               ->method('zscan')
+               ->with('key:zset', 1, array('COUNT' => 1))
+               ->will($this->returnValue(array(0, array(
+                    array('member:2nd', 2.0),
+                ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset', null, 1);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionsMatchAndCount()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('zscan')
+               ->with('key:zset', 0, array('MATCH' => 'member:*', 'COUNT' => 2))
+               ->will($this->returnValue(array(0, array(
+                    array('member:1st', 1.0), array('member:2nd', 2.0),
+               ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset', 'member:*', 2);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationWithOptionsMatchAndCountOnMultipleFetches()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->at(1))
+               ->method('zscan')
+               ->with('key:zset', 0, array('MATCH' => 'member:*', 'COUNT' => 1))
+               ->will($this->returnValue(array(1, array(
+                    array('member:1st', 1.0),
+                ))));
+        $client->expects($this->at(2))
+               ->method('zscan')
+               ->with('key:zset', 1, array('MATCH' => 'member:*', 'COUNT' => 1))
+               ->will($this->returnValue(array(0, array(
+                    array('member:2nd', 2.0),
+                ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset', 'member:*', 1);
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+
+    /**
+     * @group disconnected
+     */
+    public function testIterationRewindable()
+    {
+        $client = $this->getMock('Predis\Client', array('getProfile', 'zscan'));
+
+        $client->expects($this->any())
+               ->method('getProfile')
+               ->will($this->returnValue(ServerProfile::get('2.8')));
+        $client->expects($this->exactly(2))
+               ->method('zscan')
+               ->with('key:zset', 0, array())
+               ->will($this->returnValue(array(0, array(
+                    array('member:1st', 1.0), array('member:2nd', 2.0),
+               ))));
+
+        $iterator = new SortedSetIterator($client, 'key:zset');
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->rewind();
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:1st', $iterator->key());
+        $this->assertSame(1.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertTrue($iterator->valid());
+        $this->assertSame('member:2nd', $iterator->key());
+        $this->assertSame(2.0, $iterator->current());
+
+        $iterator->next();
+        $this->assertFalse($iterator->valid());
+    }
+}