/home/lnzliplg/public_html/tests.zip
PK��\0��#��&gmagickdraw-033_setClipPath_basic.phptnu�[���--TEST--
Test GmagickDraw, setClipPath
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function setClipPath($strokeColor, $fillColor, $backgroundColor) {

    $draw = new \GmagickDraw();
    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);
    $draw->setStrokeOpacity(1);
    $draw->setStrokeWidth(2);

    $clipPathName = 'testClipPath';

    $draw->pushClipPath($clipPathName);
    $draw->rectangle(0, 0, 250, 250);
    $draw->popClipPath();
    $draw->setClipPath($clipPathName);
    $draw->rectangle(100, 100, 400, 400);
    
    $storedPathName = $draw->getClipPath();
    
    if (strcmp($storedPathName, $clipPathName) !== 0) {
        echo "Error retrieving clipPath: $storedPathName != $clipPathName \n";
    }

    $imagick = new \Gmagick();
    $imagick->newImage(500, 500, $backgroundColor);
    $imagick->setImageFormat("png");

    $imagick->drawImage($draw);

    $bytes = $imagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

setClipPath($strokeColor, $fillColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\y��*\\?gmagickpixel-005-setcolorvaluequantum_getcolorvaluequantum.phptnu�[���--TEST--
Test setColorValueQuantum and getColorValueQuantum methods
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gp = new GMagickPixel();
echo $gp->getColorValueQuantum(Gmagick::COLOR_BLACK). "\n";
$gp->setColorValueQuantum(Gmagick::COLOR_BLACK,1);
echo $gp->getColorValueQuantum(Gmagick::COLOR_BLACK). "\n";
try {
    $gp->setColorValueQuantum(123456, 1);
    echo "GmagickPixelException was not thrown".PHP_EOL;
} catch (GmagickPixelException $gpe) {
    //This is the expected exception
    echo $gpe->getMessage();
}
?>
--EXPECTF--
0
1
Unknown color type: 123456
PK��\e��{UU,gmagick-041-set_getimageinterlacescheme.phptnu�[���--TEST--
Set, get image interlace scheme
--SKIPIF--
<?php
/* $Id: gmagick-041-set_getimageinterlacescheme.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageInterlaceScheme(1)->getImageInterlaceScheme();
?>
--EXPECTF--
1PK��\�X�  #gmagick-015-cyclecolormapimage.phptnu�[���--TEST--
Test current
--SKIPIF--
<?php
/* $Id: gmagick-015-cyclecolormapimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->cycleColorMapImage(2);
echo "ok";
?>
--EXPECTF--
okPK��\�?Jgmagick-067-minifyimage.phptnu�[���--TEST--
minifyImage test
--SKIPIF--
<?php
/* $Id: gmagick-067-minifyimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->minifyImage();
echo "ok";
?>
--EXPECTF--
ok
PK��\�j�h��.gmagickdraw-027-setantialias_getantialias.phptnu�[���--TEST--
Test and get anti-alias
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
	$gDraw = new GmagickDraw();
	$gDraw->setStrokeAntiAlias(true);
	echo $gDraw->getStrokeAntiAlias();
?>
--EXPECT--
1
PK��\����@@'gmagick-042-set_getimageiterations.phptnu�[���--TEST--
Set, get image iterations
--SKIPIF--
<?php
/* $Id: gmagick-042-set_getimageiterations.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageIterations(2)->getImageIterations();
?>
--EXPECTF--
2PK��\ؠ�E~~=gmagickdraw_040_pathCurveToQuadraticBezierAbsolute_basic.phptnu�[���--TEST--
Test GmagickDraw, pathCurveToQuadraticBezierAbsolute
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function pathCurveToQuadraticBezierAbsolute($strokeColor, $fillColor, $backgroundColor) {

    $draw = new \GmagickDraw();

    $draw->setStrokeOpacity(1);
    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);

    $draw->setStrokeWidth(2);
    $draw->setFontSize(72);

    $draw->pathStart();
    $draw->pathMoveToAbsolute(50,250);

    // This specifies a quadratic bezier curve with the current position as the start
    // point, the control point is the first two params, and the end point is the last two params.
    $draw->pathCurveToQuadraticBezierAbsolute(
        150,50, 
        250,250
    );

    // This specifies a quadratic bezier curve with the current position as the start
    // point, the control point is mirrored from the previous curves control point
    // and the end point is defined by the x, y values.
    $draw->pathCurveToQuadraticBezierSmoothAbsolute(
        450,250
    );

    // This specifies a quadratic bezier curve with the current position as the start
    // point, the control point is mirrored from the previous curves control point
    // and the end point is defined relative from the current position by the x, y values.
    $draw->pathCurveToQuadraticBezierSmoothRelative(
        200,-100
    );

    $draw->pathFinish();

    $gmagick = new \Gmagick();
    $gmagick->newImage(700, 500, $backgroundColor);
    $gmagick->setImageFormat("png");

    $gmagick->drawImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 

}

pathCurveToQuadraticBezierAbsolute($strokeColor, $fillColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\)�%%!gmagick-060-haspreviousimage.phptnu�[���--TEST--
hasPreviousImage test
--SKIPIF--
<?php
/* $Id: gmagick-060-haspreviousimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->hasPreviousImage();
echo "ok";
?>
--EXPECTF--
ok
PK��\������#gmagick-102-unsharpenmaskimage.phptnu�[���--TEST--
unsharpmaskimage test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->unsharpMaskImage(0 , 0.5 , 1 , 0.05);
echo "ok";
?>
--EXPECTF--
ok
PK��\�rKdEE!gmagick-092-getimagegeometry.phptnu�[���--TEST--
setImage test
--SKIPIF--
<?php
/* $Id: gmagick-086-setcompressionquality.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
print_r($image->getImageGeometry());
?>
--EXPECTF--
Array
(
    [width] => 70
    [height] => 46
)
PK��\oU�h��gmagick-105-readimageblob.phptnu�[���--TEST--
Gmagick::readImageBlob
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->setImageFormat('png');
$imageBlob = $image->getImageBlob();

$imageReload = new Gmagick();
$imageReload->readImageBlob($imageBlob);
printf(
    "Dimensions are %d x %d\n",
    $imageReload->getImageWidth(),
    $imageReload->getImageHeight()
);
echo "ok";

?>
--EXPECTF--
Dimensions are 70 x 46
okPK��\�?�FF6gmagickdraw-012-setstrokeopacity_getstrokeopacity.phptnu�[���--TEST--
Test setstrokeopacity, getstrokeopacity
--SKIPIF--
<?php
/* $Id: gmagickdraw-012-setstrokeopacity_getstrokeopacity.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gd = new GMagickDraw();
echo $gd->setStrokeOpacity(0.8)->getStrokeOpacity();
?>
--EXPECT--
0.8PK��\}��00"gmagick-037-set_getimagegamma.phptnu�[���--TEST--
Set, get image gamma
--SKIPIF--
<?php
/* $Id: gmagick-037-set_getimagegamma.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageGamma(0.2)->getImageGamma();
?>
--EXPECTF--
0.2PK��\,�`��(gmagick-027-set_getimagebordercolor.phptnu�[���--TEST--
Imagebordercolor test
--SKIPIF--
<?php
/* $Id: gmagick-027-set_getimagebordercolor.phpt 331869 2013-10-20 11:27:00Z remi $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH != 8) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$color=new GmagickPixel("rgb(255,255,255)");
$pixel = $gm->setImageBorderColor($color)->getImageBorderColor();
print_r($pixel->getColor());
?>
--EXPECTF--
rgb(255,255,255)
PK��\>~22gmagick-075-quantizeimages.phptnu�[���--TEST--
quantizeImages test
--SKIPIF--
<?php
/* $Id: gmagick-075-quantizeimages.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->quantizeImages(2, 2, 2, true, true);
echo "ok";
?>
--EXPECTF--
ok
PK��\L̚�gmagick-083-solarizeimage.phptnu�[���--TEST--
solarizeImage test
--SKIPIF--
<?php
/* $Id: gmagick-083-solarizeimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->solarizeImage(1);
echo "ok";
?>
--EXPECTF--
okPK��\(A�;;(gmagickdraw_045_setFontFamily_basic.phptnu�[���--TEST--
Test GmagickDraw, setFontFamily
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function setFontFamily($fillColor, $strokeColor, $backgroundColor) {

    $draw = new \GmagickDraw();

    $strokeColor = new \GmagickPixel($strokeColor);
    $fillColor = new \GmagickPixel($fillColor);

    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);
    //TODO - actually use setFontFamily
    $draw->setStrokeWidth(2);

    $draw->setFontSize(24);
    $draw->annotate(50, 50, "Lorem Ipsum!");

    $draw->setFontSize(36);
    $draw->annotate(50, 100, "Lorem Ipsum!");

    $draw->setFontSize(48);
    $draw->annotate(50, 150, "Lorem Ipsum!");

    $draw->setFontSize(60);
    $draw->annotate(50, 200, "Lorem Ipsum!");

    $draw->setFontSize(72);
    $draw->annotate(50, 250, "Lorem Ipsum!");

    $gmagick = new \Gmagick();
    $gmagick->newImage(500, 500, $backgroundColor);
    $gmagick->setImageFormat("png");
    $gmagick->drawImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

setFontFamily($fillColor, $strokeColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\�!�"gmagick-126_negateImage_basic.phptnu�[���--TEST--
Test Gmagick, negateImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$grayOnly = 0;
$channel = Gmagick::CHANNEL_DEFAULT;

function negateImage($grayOnly, $channel) {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->negateImage($grayOnly, $channel);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

negateImage($grayOnly, $channel) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\����pp'gmagick-044-set_getimageredprimary.phptnu�[���--TEST--
Set, get imageredprimary
--SKIPIF--
<?php
/* $Id: gmagick-044-set_getimageredprimary.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
print_r($gm->setImageRedPrimary(0.2, 0.2)->getImageRedPrimary());
?>
--EXPECTF--
Array
(
    [x] => 0.2
    [y] => 0.2
)PK��\�+"�&&gmagick-013-commentimage.phptnu�[���--TEST--
Test commentImage
--SKIPIF--
<?php
/* $Id: gmagick-013-commentimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->commentImage("Test Comment");
echo "ok";
?>
--EXPECT--
ok
PK��\pn>>.gmagickdraw-010-setfontstyle_getfontstyle.phptnu�[���--TEST--
Test setfontstyle, getfontstyle
--SKIPIF--
<?php
/* $Id: gmagickdraw-010-setfontstyle_getfontstyle.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gd = new GMagickDraw();
echo $gd->setFontStyle(Gmagick::STYLE_NORMAL)->getFontStyle();
?>
--EXPECT--
0PK��\˺�y** gmagick-070-motionblurimage.phptnu�[���--TEST--
motionblur test
--SKIPIF--
<?php
/* $Id: gmagick-070-motionblurimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->motionBlurImage(0.2, 0.2, 0.2);
echo "ok";
?>
--EXPECTF--
ok
PK��\,��@@gmagick-090-setimage.phptnu�[���--TEST--
setImage test
--SKIPIF--
<?php
/* $Id: gmagick-086-setcompressionquality.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$replace = new Gmagick('magick:rose');
$image->setImage($replace);
echo "1";
?>
--EXPECTF--
1
PK��\R�*gmagick-093-flattenimages.phptnu�[���--TEST--
setImage test
--SKIPIF--
<?php
/* $Id: gmagick-086-setcompressionquality.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->flattenImages();
echo "ok";
?>
--EXPECTF--
ok
PK��\6,��mmgmagickdraw-025-affine.phptnu�[���--TEST--
Test affine
--SKIPIF--
<?php
/* $Id: gmagickdraw-025-affine.phpt 280206 2010-08-07 12:46:00Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
    $gDraw = new GmagickDraw();
    $affineArray = array("sx" => 1, "rx" => 0, "ry" => 0, "sy" =>1, "tx" => 0, "ty" => 0);
    $gDraw->affine($affineArray);
    echo "ok";
?>
--EXPECT--
ok
PK��\)jN	00.gmagick-025-set_and_getimageckgroundcolor.phptnu�[���--TEST--
Test setImageBackgroundColor and getImageBackgroundColor methods
--SKIPIF--
<?php
/* $Id: gmagick-025-set_and_getimageckgroundcolor.phpt 331869 2013-10-20 11:27:00Z remi $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH != 8) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$color=new GmagickPixel("rgb(255,255,255)");
$pixel =  $gm->setImageBackgroundColor($color)->getimagebackgroundcolor();
print_r($pixel->getColor());
?>
--EXPECTF--
rgb(255,255,255)
PK��\���gmagick-021-equalizeimage.phptnu�[���--TEST--
Equalize
--SKIPIF--
<?php
/* $Id: gmagick-021-equalizeimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->equalizeImage();
echo "ok";
?>
--EXPECTF--
okPK��\��Q--+gmagickdraw_042_setTextAntialias_basic.phptnu�[���--TEST--
Test GmagickDraw, setTextAntialias
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function setTextAntialias($fillColor, $backgroundColor) {

    $draw = new \GmagickDraw();
    $draw->setStrokeColor('none');
    $draw->setFillColor($fillColor);
    $draw->setStrokeWidth(1);
    $draw->setFontSize(32);
    $draw->setTextAntialias(false);
    $draw->annotate(5, 30, "Lorem Ipsum!");
    $draw->setTextAntialias(true);
    $draw->annotate(5, 65, "Lorem Ipsum!");

    $currentValue = $draw->getTextAntialias();

    if ($currentValue !== true) {
        echo "Failed to get textAntiAlias setting, which should be true\n";
        var_dump($currentValue);
    }

    $gmagick = new \Gmagick();
    $gmagick->newImage(220, 80, $backgroundColor);
    $gmagick->setImageFormat("png");
    $gmagick->drawImage($draw);

    //Scale the image so that people can see the aliasing.
    $gmagick->scaleImage(220 * 6, 80 * 6);
    $gmagick->cropImage(640, 480, 0, 0);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

setTextAntialias($fillColor, $backgroundColor);
echo "Ok";
?>
--EXPECTF--
OkPK��\%q�3tt%gmagick-038-setimagegreenprimary.phptnu�[���--TEST--
Set, get imagegreenprimary
--SKIPIF--
<?php
/* $Id: gmagick-038-setimagegreenprimary.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
print_r($gm->setImageGreenPrimary(0.2, 0.2)->getImageGreenPrimary());
?>
--EXPECTF--
Array
(
    [x] => 0.2
    [y] => 0.2
)PK��\��t�``'gmagickdraw_035_setClipUnits_basic.phptnu�[���--TEST--
Test GmagickDraw, setClipUnits
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function setClipUnits($strokeColor, $fillColor, $backgroundColor) {

    $draw = new \GmagickDraw();

    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);
    $draw->setStrokeOpacity(1);
    $draw->setStrokeWidth(2);
    $clipPathName = 'testClipPath';
    $draw->setClipUnits(\Gmagick::RESOLUTION_PIXELSPERINCH);
    $draw->pushClipPath($clipPathName);
    $draw->rectangle(0, 0, 250, 250);
    $draw->popClipPath();
    $draw->setClipPath($clipPathName);
    $clipUnits = $draw->getClipUnits();
    if ($clipUnits !== \Gmagick::RESOLUTION_PIXELSPERINCH) {
        echo "Failed to get correct clipUnits $clipRule != \Gmagick::RESOLUTION_PIXELSPERINCH \n";
    }

    //RESOLUTION_PIXELSPERINCH
    //RESOLUTION_PIXELSPERCENTIMETER

    $draw->rectangle(200, 200, 300, 300);
    $imagick = new \Gmagick();
    $imagick->newImage(500, 500, $backgroundColor);
    $imagick->setImageFormat("png");

    $imagick->drawImage($draw);

    $bytes = $imagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

setClipUnits($strokeColor, $fillColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\�۬�//gmagick-074-quantizeimage.phptnu�[���--TEST--
quantizeImage test
--SKIPIF--
<?php
/* $Id: gmagick-074-quantizeimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->quantizeImage(2, 2, 2, true, true);
echo "ok";
?>
--EXPECTF--
ok
PK��\���!��gmagick-097-sampleimage.phptnu�[���--TEST--
sampleimage test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->sampleimage(5, 5);
echo "ok";
?>
--EXPECTF--
ok
PK��\����LL0gmagickdraw-030-setmiterlimit_getmiterlimit.phptnu�[���--TEST--
Test get and set strokeMiterLimit
--SKIPIF--
<?php
/* $Id: gmagickdraw30-setmiterlimit_getmiterlimit.phpt 280206 2013-11-05 12:46:00Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
	$gDraw = new GmagickDraw();
	$gDraw->setStrokeMiterLimit(2);
	echo $gDraw->getStrokeMiterLimit();
?>
--EXPECT--
2
PK��\��fgmagick-023-gammaimage.phptnu�[���--TEST--
Gamma image
--SKIPIF--
<?php
/* $Id: gmagick-023-gammaimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->gammaImage(0.8);
echo "1";
?>
--EXPECTF--
1PK��\�om��
bug63677.phptnu�[���--TEST--
Test bug 63677 - getimagehistogram
--SKIPIF--
<?php

if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php

class Image
{
    private $gmagick;
    private $layers;

    public function __construct(\Gmagick $gmagick)
    {
        $this->gmagick = $gmagick;
        $this->layers = new Layers($this, $this->gmagick);
    }

    public function __destruct()
    {
        if (null !== $this->gmagick && $this->gmagick instanceof \Gmagick) {
            $this->gmagick->clear();
            $this->gmagick->destroy();
        }
    }

    public function histogram()
    {
        return array_map(
            function(\GmagickPixel $pixel) {
                return new \stdClass();
            },
            $this->gmagick->getimagehistogram()
        );
    }
}

class Layers
{
    private $image;
    private $resource;

    public function __construct(Image $image, \Gmagick $resource)
    {
        $this->image = $image;
        $this->resource = $resource;
    }
}


for ($i=0; $i<5; $i++) {
    $image = new Image(new \Gmagick("magick:logo"));
    // the same fixture must beloaded twice to reprodcue the seg fault
    $image = new Image(new \Gmagick("magick:logo"));

    $image->histogram();
}

echo "ok";
?>
--EXPECT--
ok
PK��\v�gmagick-062-labelimage.phptnu�[���--TEST--
labelImage test
--SKIPIF--
<?php
/* $Id: gmagick-062-labelimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->labelImage("test");
echo "ok";
?>
--EXPECTF--
ok
PK��\�i� ��gmagickdraw-021-rectangle.phptnu�[���--TEST--
Test rectangle
--SKIPIF--
<?php
/* $Id: gmagickdraw-021-rectangle.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");
$gd->rectangle(20,20,30,30);
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\�zZ,22gmagick-073-profileimage.phptnu�[���--TEST--
profileImage test
--SKIPIF--
<?php
/* $Id: gmagick-073-profileimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->profileImage('icc', 'test_icc_profile');
echo "ok";
?>
--EXPECTF--
ok
PK��\���gmagick-086-swirlimage.phptnu�[���--TEST--
swirlImage test
--SKIPIF--
<?php
/* $Id: gmagick-086-swirlimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->swirlImage(0.2);
echo "ok";
?>
--EXPECTF--
okPK��\pQ���$gmagickdraw_039_pathStart_basic.phptnu�[���--TEST--
Test GmagickDraw, pathStart
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function pathStart($strokeColor, $fillColor, $backgroundColor) {

    $draw = new \GmagickDraw();

    $draw->setStrokeOpacity(1);
    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);

    $draw->setStrokeWidth(2);
    $draw->setFontSize(72);

    $draw->pathStart();
    $draw->pathMoveToAbsolute(50, 50);
    $draw->pathLineToAbsolute(100, 50);
    $draw->pathLineToRelative(0, 50);
    $draw->pathLineToHorizontalRelative(-50);
    $draw->pathFinish();

    $draw->pathStart();
    $draw->pathMoveToAbsolute(50, 50);
    $draw->pathMoveToRelative(300, 0);
    $draw->pathLineToRelative(50, 0);
    $draw->pathLineToVerticalRelative(50);
    $draw->pathLineToHorizontalAbsolute(350);
    $draw->pathclose();
    $draw->pathFinish();

    $draw->pathStart();
    $draw->pathMoveToAbsolute(50, 300);
    $draw->pathCurveToAbsolute(50, 300, 100, 200, 300, 300);
    $draw->pathLineToVerticalAbsolute(350);
    $draw->pathFinish();

    $gmagick = new \Gmagick();
    $gmagick->newImage(500, 500, $backgroundColor);
    $gmagick->setImageFormat("png");

    $gmagick->drawImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

pathStart($strokeColor, $fillColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\/���gmagick-017-destroy.phptnu�[���--TEST--
Destroy
--SKIPIF--
<?php
/* $Id: gmagick-017-destroy.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->destroy();
echo "ok";
?>
--EXPECTF--
okPK��\�P??2gmagickdraw-007-setfillopacity_getfillopacity.phptnu�[���--TEST--
Test setfillopacity, getfillopacity
--SKIPIF--
<?php
/* $Id: gmagickdraw-007-setfillopacity_getfillopacity.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gd = new GMagickDraw();
$gd->setFillOpacity(0.8);
echo $gd->getFillOpacity();
?>
--EXPECT--
0.2PK��\٣;}nngmagick-011-charcoalimage.phptnu�[���--TEST--
Test charcoal image
--SKIPIF--
<?php
/* $Id: gmagick-011-charcoalimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm->read("magick:rose");
$gm->charcoalImage(3,3);
$gm->write($filename);
echo "ok";
?>
--EXPECT--
okPK��\�i�ˆ� gmagick-114_convolveImage_6.phptnu�[���--TEST--
Test Gmagick, convolveImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
$imageMagickBelowVersion=0x700;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$bias = 0.5;
$kernelMatrix = array (
  0 => -1,
  1 => -1,
  2 => -1,
  3 => -1,
  4 => 8,
  5 => -1,
  6 => -1,
  7 => -1,
  8 => -1,
);

function convolveImage($bias, $kernelMatrix) {
    $gmagick = new \Gmagick("magick:logo");
    
    
    $gmagick->convolveImage($kernelMatrix);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

convolveImage($bias, $kernelMatrix) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\�`0��&gmagickdraw_037_setFillRule_basic.phptnu�[���--TEST--
Test GmagickDraw, setFillRule
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function setFillRule($fillColor, $strokeColor, $backgroundColor) {

    $draw = new \GmagickDraw();

    $draw->setStrokeWidth(1);
    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);

    $fillRules = array(\Gmagick::FILLRULE_NONZERO, \Gmagick::FILLRULE_EVENODD);

    $points = 11;
    $size = 150;

    $draw->translate(175, 160);

    for ($x = 0; $x < 2; $x++) {
        $draw->setFillRule($fillRules[$x]);
        $draw->pathStart();
        for ($n = 0; $n < $points * 2; $n++) {

            if ($n >= $points) {
                $angle = fmod($n * 360 * 4 / $points, 360) * pi() / 180;
            }
            else {
                $angle = fmod($n * 360 * 3 / $points, 360) * pi() / 180;
            }

            $positionX = $size * sin($angle);
            $positionY = $size * cos($angle);

            if ($n == 0) {
                $draw->pathMoveToAbsolute($positionX, $positionY);
            }
            else {
                $draw->pathLineToAbsolute($positionX, $positionY);
            }
        }

        $draw->pathClose();
        $draw->pathFinish();
        
        $currentFillRule = $draw->getFillRule();
        
        if ($currentFillRule !== $fillRules[$x]) {
            echo "Failed to get correct fillRule $currentFillRule != ".$fillRules[$x]." \n";
        }

        $draw->translate(325, 0);
    }

    $image = new \Gmagick();
    $image->newImage(700, 320, $backgroundColor);
    $image->setImageFormat("png");
    $image->drawImage($draw);

    $bytes = $image->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

setFillRule($fillColor, $strokeColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\!��gmagick-016-despeckleimage.phptnu�[���--TEST--
Despeckle image
--SKIPIF--
<?php
/* $Id: gmagick-016-despeckleimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->despeckleImage();
echo "ok";
?>
--EXPECTF--
okPK��\>��DD'gmagick-029-set_getimagecolorspace.phptnu�[���--TEST--
Set, get imagecolorspace test
--SKIPIF--
<?php
/* $Id: gmagick-029-set_getimagecolorspace.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageColorspace(2)->getImageColorspace();
?>
--EXPECTF--
2PK��\m�x�662gmagickdraw-014-setstrokewidth_getstrokewidth.phptnu�[���--TEST--
Test setstrokewidth, getstrokewidth
--SKIPIF--
<?php
/* $Id: gmagickdraw-014-setstrokewidth_getstrokewidth.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gd = new GMagickDraw();
echo $gd->setStrokeWidth(2)->getStrokeWidth();
?>
--EXPECT--
2PK��\qE**"gmagick-066-medianfilterimage.phptnu�[���--TEST--
medianFilterImage test
--SKIPIF--
<?php
/* $Id: gmagick-066-medianfilterimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->medianFilterImage(0.5);
echo "ok";
?>
--EXPECTF--
okPK��\��*���#gmagickdraw-023-roundrectangle.phptnu�[���--TEST--
Test round rectangle
--SKIPIF--
<?php
/* $Id: gmagickdraw-023-roundrectangle.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");
$gd->roundRectangle(30,30,35,35,5,5);
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\�A���gmagickdraw-004-arc.phptnu�[���--TEST--
Test arc
--SKIPIF--
<?php
/* $Id: gmagickdraw-004-arc.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");
$gd->arc(10, 10, 30, 30, 5, 10);
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\�#�X''!gmagick-049-set_getimagetype.phptnu�[���--TEST--
Set, get imagetype
--SKIPIF--
<?php
/* $Id: gmagick-049-set_getimagetype.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageType(6)->getImageType();
?>
--EXPECTF--
6PK��\���Dgmagick-061-implodeimage.phptnu�[���--TEST--
implode image test
--SKIPIF--
<?php
/* $Id: gmagick-061-implodeimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->implodeImage(0.5);
echo "ok";
?>
--EXPECTF--
ok
PK��\�(W��gmagickdraw-005-bezier.phptnu�[���--TEST--
Test bezier
--SKIPIF--
<?php
/* $Id: gmagickdraw-005-bezier.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");
$gd->bezier( array(
                    array( "x" => 3 , "y" => 8 ),
                    array( "x" => 13, "y" => 16 ),
                    array( "x" => 20, "y" => 25 ),
                    array( "x" => 25, "y" => 23 ),
                    array( "x" => 33, "y" => 23 ),
                    array( "x" => 33, "y" => 3 ),
                 ) );
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\�(�gmagick-080-scaleimage.phptnu�[���--TEST--
scaleImage test
--SKIPIF--
<?php
/* $Id: gmagick-080-scaleimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->scaleImage(5, 5);
echo "ok";
?>
--EXPECTF--
okPK��\K�{��#gmagick-096-setinterlacescheme.phptnu�[���--TEST--
setInterlaceScheme test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->setInterlaceScheme(Gmagick::INTERLACE_LINE);
echo "ok";
?>
--EXPECTF--
ok
PK��\��d**gmagick-005-draw.phptnu�[���--TEST--
Test read/write
--SKIPIF--
<?php
/* $Id: gmagick-005-draw.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$color = "rgb(255,255,123)";
$pixel = new GmagickPixel($color);
$gd = new GMagickDraw();
$gd->setStrokeColor($pixel)->setStrokeWidth( 2 )->setFillColor($pixel)->ellipse( 200, 100, 50, 50, 0, 360 );
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick("magick:rose");
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
ok
PK��\�Y	))"gmagick-040-getimagehistogram.phptnu�[���--TEST--
Set, get image histogram
--SKIPIF--
<?php
/* $Id: gmagick-040-getimagehistogram.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->getImageHistogram();
echo "ok";
?>
--EXPECTF--
okPK��\��k�gmagick-039-getimageheight.phptnu�[���--TEST--
Set, get image height
--SKIPIF--
<?php
/* $Id: gmagick-039-getimageheight.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->getImageHeight();
?>
--EXPECTF--
46PK��\bi
\��gmagick-099-appendimages.phptnu�[���--TEST--
appendimages test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->newImage(100, 50, "blue");
$image->appendImages();
echo "ok";
?>
--EXPECTF--
ok
PK��\�a����2gmagickdraw-013-setstrokecolor_getstrokecolor.phptnu�[���--TEST--
Test setstrokecolor, getstrokecolor
--SKIPIF--
<?php
/* $Id: gmagickdraw-013-setstrokecolor_getstrokecolor.phpt 331869 2013-10-20 11:27:00Z remi $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH != 8) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$color = "rgb(255,255,255)";
$pixel = new GmagickPixel($color);
$gd = new GMagickDraw();
print_r($gd->setStrokeColor($pixel)->getStrokeColor()->getColor());
?>
--EXPECT--
rgb(255,255,255)
PK��\�+iB&gmagickdraw_036_pushPattern_basic.phptnu�[���--TEST--
Test GmagickDraw, pushPattern
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function pushPattern($strokeColor, $fillColor, $backgroundColor) {
    $draw = new \GmagickDraw();

    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);
    $draw->setStrokeWidth(1);
    $draw->setStrokeOpacity(1);
    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);

    $draw->setStrokeWidth(1);

    $draw->pushPattern("MyFirstPattern", 0, 0, 50, 50);
    for ($x = 0; $x < 50; $x += 10) {
        for ($y = 0; $y < 50; $y += 5) {
            $positionX = $x + (($y / 5) % 5);
            $draw->rectangle($positionX, $y, $positionX + 5, $y + 5);
        }
    }
    $draw->popPattern();

    $draw->setFillOpacity(0);
    $draw->rectangle(100, 100, 400, 400);
    $draw->setFillOpacity(1);

    $draw->setFillOpacity(1);

    $draw->pushGraphicContext();
    $draw->setFillPatternURL('#MyFirstPattern');
    $draw->setFillColor('yellow');
    $draw->rectangle(100, 100, 400, 400);
    $draw->popGraphicContext();

    $gmagick = new \Gmagick();
    $gmagick->newImage(500, 500, $backgroundColor);
    $gmagick->setImageFormat("png");

    $gmagick->drawImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

pushPattern($strokeColor, $fillColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\������,gmagick-027-set_getimagebordercolor_q16.phptnu�[���--TEST--
Imagebordercolor test
--SKIPIF--
<?php
/* $Id: gmagick-027-set_getimagebordercolor.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH != 16) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$color=new GmagickPixel("rgb(255,255,255)");
$pixel = $gm->setImageBorderColor($color)->getImageBorderColor();
print_r($pixel->getColor());
?>
--EXPECTF--
rgb(65535,65535,65535)
PK��\+��gmagick-091-getimage.phptnu�[���--TEST--
getImage test
--SKIPIF--
<?php
/* $Id: gmagick-086-getimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->getImage();
echo "1";
?>
--EXPECTF--
1
PK��\��v��'gmagick-122_unsharpMaskImage_basic.phptnu�[���--TEST--
Test Gmagick, unsharpMaskImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$radius = 5;
$sigma = 1;
$amount = 5;
$unsharpThreshold = 0;
$channel = Gmagick::CHANNEL_DEFAULT;

function unsharpMaskImage($radius, $sigma, $amount, $unsharpThreshold) {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->unsharpMaskImage($radius, $sigma, $amount, $unsharpThreshold);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

unsharpMaskImage($radius, $sigma, $amount, $unsharpThreshold) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\(}t�  gmagick-053-getpackagename.phptnu�[���--TEST--
get packagename
--SKIPIF--
<?php
/* $Id: gmagick-053-getpackagename.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->getPackageName();
?>
--EXPECTF--
GraphicsMagickPK��\P(P�DD gmagick-035-set_getfilename.phptnu�[���--TEST--
Set, get filename
--SKIPIF--
<?php
/* $Id: gmagick-035-set_getfilename.phpt 295011 2010-02-13 17:06:23Z mkoppanen $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setFilename("magickrose.png")->getFilename();
?>
--EXPECTF--
magickrose.pngPK��\62�՜�gmagick-065-mapimage.phptnu�[���--TEST--
mapImage test
--SKIPIF--
<?php
/* $Id: gmagick-065-mapimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH == 8) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$gm = new Gmagick();
$gm2 = new Gmagick();
$gm->read("magick:rose");
$gm2->read("magick:rose");
$gm->mapImage($gm2, true);
echo "ok";
?>
--EXPECTF--
ok
PK��\�2K�gmagick-019-embossimage.phptnu�[���--TEST--
Emboss
--SKIPIF--
<?php
/* $Id: gmagick-019-embossimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->embossImage(0.8, 0.5);
echo "ok";
?>
--EXPECTF--
okPK��\I.eN��"gmagick-003-resize_variations.phptnu�[���--TEST--
Test resize
--SKIPIF--
<?php
/* $Id: gmagick-003-resize_variations.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick("magick:rose");
$gm->resizeImage(10, 10, Gmagick::FILTER_LANCZOS, 0.5);
$gm->resizeImage(10, 10, Gmagick::FILTER_UNDEFINED, 0.5, true);
$gm->resizeImage(10, 10, Gmagick::FILTER_GAUSSIAN, 0.5, false);
echo "ok";
?>
--EXPECTF--
ok
PK��\#�	�OO)gmagick-028-set_getimagechanneldepth.phptnu�[���--TEST--
Set, get imagechanneldepth test
--SKIPIF--
<?php
/* $Id: gmagick-028-set_getimagechanneldepth.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageChannelDepth(1,1)->getImageChannelDepth(1);
?>
--EXPECTF--
1PK��\�0�Qgmagick-100-coalesceimages.phptnu�[���--TEST--
coalesceimages test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->newImage(100, 50, "blue");
$image->newImage(100, 50, "yellow");
$image->coalesceImages();
echo "ok";
?>
--EXPECTF--
ok
PK��\���gmagick-010-borderimage.phptnu�[���--TEST--
Test border image
--SKIPIF--
<?php
/* $Id: gmagick-010-borderimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm->read("magick:rose");
$color=new GmagickPixel("rgb(255,255,255)");
$gm->borderImage($color,3,3);
$gm->write($filename);
echo "ok";
?>
--EXPECT--
okPK��\4jȴ��gmagick-101-sharpenimage.phptnu�[���--TEST--
sharpenimage test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->sharpenImage(1.5, 1.2);
echo "ok";
?>
--EXPECTF--
ok
PK��\���**,gmagickdraw_044_setTextUnderColor_basic.phptnu�[���--TEST--
Test GmagickDraw, setTextUnderColor
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';
$textUnderColor = 'DeepPink2';

function setTextUnderColor($strokeColor, $fillColor, $backgroundColor, $textUnderColor) {
    $draw = new \GmagickDraw();

    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);
    $draw->setStrokeWidth(2);
    $draw->setFontSize(72);
    $draw->annotate(50, 75, "Lorem Ipsum!");
    $draw->setTextUnderColor($textUnderColor);
    $draw->annotate(50, 175, "Lorem Ipsum!");

    $gmagick = new \Gmagick();
    $gmagick->newImage(500, 500, $backgroundColor);
    $gmagick->setImageFormat("png");
    $gmagick->drawImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

setTextUnderColor($strokeColor, $fillColor, $backgroundColor, $textUnderColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\�[-0'gmagick-103-setgetimagecompression.phptnu�[���--TEST--
setImageCompression test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->setImageCompression(Gmagick::COMPRESSION_JPEG);
echo $image->getImageCompression();
?>
--EXPECTF--
5PK��\����SS%gmagick-036-set_getimagefilename.phptnu�[���--TEST--
Set, get imagefilename
--SKIPIF--
<?php
/* $Id: gmagick-036-set_getimagefilename.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageFilename('magickrose.png')->getImageFilename();
?>
--EXPECTF--
magickrose.pngPK��\4�-���.gmagickdraw-006-setfillcolor_getfillcolor.phptnu�[���--TEST--
Test setfillcolor, getfillcolor
--SKIPIF--
<?php
/* $Id: gmagickdraw-006-setfillcolor_getfillcolor.phpt 331869 2013-10-20 11:27:00Z remi $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH != 8) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$color = "rgb(255,255,255)";
$pixel = new GmagickPixel($color);
$gd = new GMagickDraw();
print_r($gd->setFillColor($pixel)->getFillColor()->getColor());
?>
--EXPECT--
rgb(255,255,255)
PK��\~:}gmagick-076-queryfonts.phptnu�[���--TEST--
queryFonts test
--SKIPIF--
<?php
/* $Id: gmagick-076-queryfonts.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->queryFonts();
echo "ok";
?>
--EXPECTF--
ok
PK��\ԱsFF4gmagickdraw-016-settextencoding_gettextencoding.phptnu�[���--TEST--
Test settextencoding, gettextencoding
--SKIPIF--
<?php
/* $Id: gmagickdraw-016-settextencoding_gettextencoding.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gd = new GMagickDraw();
echo $gd->setTextEncoding("UTF-8")->getTextEncoding();
?>
--EXPECT--
UTF-8PK��\9M�`gmagick-084-spreadimage.phptnu�[���--TEST--
spreadImage test
--SKIPIF--
<?php
/* $Id: gmagick-084-spreadimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->spreadImage(0.2);
echo "ok";
?>
--EXPECTF--
okPK��\	���YY$gmagick-043-set_getimageprofile.phptnu�[���--TEST--
Set, get imageprofile
--SKIPIF--
<?php
/* $Id: gmagick-043-set_getimageprofile.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageProfile("test", "test_profile")->getImageProfile("test");
?>
--EXPECTF--
test_profilePK��\�v�dd8gmagickdraw-015-settextdecoration_gettextdecoration.phptnu�[���--TEST--
Test settextdecoration, gettextdecoration
--SKIPIF--
<?php
/* $Id: gmagickdraw-015-settextdecoration_gettextdecoration.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gd = new GMagickDraw();
echo $gd->setTextDecoration(Gmagick::DECORATION_UNDERLINE)->getTextDecoration();
?>
--EXPECT--
1PK��\�ƂE00$gmagick-034-set_getimagedispose.phptnu�[���--TEST--
Set, getimagedepth
--SKIPIF--
<?php
/* $Id: gmagick-034-set_getimagedispose.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageDispose(2)->getImageDispose();
?>
--EXPECTF--
2PK��\�[��
skipif.incnu�[���<?php

if (!extension_loaded("gmagick")) die("skip");

function checkClassMethods($class, $methods)
{
	foreach ($methods as $method) {
		if (method_exists($class, $method) == false) {
			die("skip Class method $class::$method not present");
		}
	}
}
PK��\H;�Vgmagick-014-current.phptnu�[���--TEST--
Test current
--SKIPIF--
<?php
/* $Id: gmagick-014-current.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm2 = $gm->current();
echo "ok";
?>
--EXPECTF--
okPK��\<"�NEE)gmagick-024-getcopyright_getfilename.phptnu�[���--TEST--
Test getCopyright, getFileName
--SKIPIF--
<?php
/* $Id: gmagick-024-getcopyright_getfilename.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->getCopyright();
$gm->getFilename();
echo "ok";
?>
--EXPECTF--
okPK��\�B�[gmagick-072-oilpaintimage.phptnu�[���--TEST--
oilPaintImage test
--SKIPIF--
<?php
/* $Id: gmagick-072-oilpaintimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->oilPaintImage(0.2);
echo "ok";
?>
--EXPECTF--
ok
PK��\�6�h88#gmagick-071-next_previousimage.phptnu�[���--TEST--
next,previousImage test
--SKIPIF--
<?php
/* $Id: gmagick-071-next_previousimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->nextImage();
$gm->previousImage();
echo "ok";
?>
--EXPECTF--
ok
PK��\Wr���gmagick-113_compareImages.phptnu�[���--TEST--
Test PHP bug #59378 writing to php://memory is incomplete
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$gmagick = new Gmagick("magick:logo");
//$gmagick->setFormat("png");

$fp = fopen("php://memory", 'r+');
$gmagick->writeImageFile($fp);
rewind($fp);
$memoryBlob = stream_get_contents($fp);
fclose($fp);

//This test depends on getImageBlob working correctly.
$imageBlob = $gmagick->getImageBlob();

//Read the images from the data blobs.
$imageReopened = new Gmagick();
$imageReopened->readImageBlob($imageBlob);
$memoryReopened = new Gmagick();
$memoryReopened->readImageBlob($memoryBlob);

//Compare to see if they are identical.
$result = $imageReopened->compareImages($memoryReopened, \Gmagick::METRIC_MEANABSOLUTEERROR);

if ($result[1] == 0) {
    echo "Reopened images are identical.";
}
else {
    echo "Error, reopened images have changed.";
    var_dump($result);
}

?>
--EXPECTF--
Reopened images are identical.
PK��\�\�>>
bug71626.phptnu�[���--TEST--
Test bug 71626 - multiple calls to queryFontMetrics
--SKIPIF--
<?php

if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php

$textWidth = null;

for ($i=0; $i<5; $i++) {
	$image = new Gmagick();
	$fontDraw = new GmagickDraw();
	$fontMetrics = $image->queryFontMetrics($fontDraw, 'g');

	if (is_array($fontMetrics) === false ||
		isset($fontMetrics['textWidth']) === false) {
		echo "fontMetrics contains bad data".PHP_EOL;
		var_dump($fontMetrics);
	}
}


for ($i=0; $i<5; $i++) {
	$gmagick = new \Gmagick("magick:logo");
	$fontDraw = new GmagickDraw();
	$fontMetrics = $image->queryFontMetrics($fontDraw, 'g');

	if (is_array($fontMetrics) === false ||
		isset($fontMetrics['textWidth']) === false) {
		echo "fontMetrics contains bad data".PHP_EOL;
		var_dump($fontMetrics);
	}
}


echo "ok";
?>
--EXPECT--
ok
PK��\�Vb11%gmagick-081-separateimagechannel.phptnu�[���--TEST--
separateImageChannel test
--SKIPIF--
<?php
/* $Id: gmagick-081-separateimagechannel.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->separateImageChannel(1);
echo "ok";
?>
--EXPECTF--
okPK��\�Z4}��%gmagick-121_deconstructGif_basic.phptnu�[���--TEST--
Test Tutorial, deconstructGif
--SKIPIF--
<?php

require_once(dirname(__FILE__) . '/skipif.inc');

checkClassMethods("GmagickDraw", array('circle', 'translate'));

?>
--XFAIL--
GraphicMagick has multiple issues with 'image counts'. It appears to be confused how many images are in the list. 
--FILE--
<?php

$aniGif = new \Gmagick();
$aniGif->setFormat("gif");

$circleRadius = 20;
$imageFrames = 6;
$imageSize = 200;

$background = new \Gmagick();
$background->newImage($imageSize, $imageSize, "gray");

$blackWhite = new \Gmagick();
$blackWhite->newImage($imageSize, $imageSize, "white");

$backgroundPalette = clone $background;
$backgroundPalette->quantizeImage(240, \Gmagick::COLORSPACE_RGB, 8, false, false);

$blackWhitePalette = clone $blackWhite;
$blackWhitePalette->quantizeImage(16, \Gmagick::COLORSPACE_RGB, 8, false, false);

$backgroundPalette->addimage($blackWhitePalette);

/*
for($count=0 ; $count<$imageFrames ; $count++) {
	echo "Frame: ".$count."\n";
	$drawing = new \GmagickDraw();
	$drawing->setFillColor('white');
	$drawing->setStrokeColor('rgba(64, 64, 64, 0.8)');
	$strokeWidth = 4;
	$drawing->setStrokeWidth($strokeWidth);

	$distanceToMove = $imageSize + (($circleRadius + $strokeWidth) * 2);
	$offset = ($distanceToMove * $count / ($imageFrames -1)) - ($circleRadius + $strokeWidth);
	$drawing->translate(
		 $offset,
		 ($imageSize / 2) + ($imageSize / 3 * cos(20 * $count / $imageFrames))
	);
	$drawing->circle(0, 0, $circleRadius, 0);

	$frame = clone $background;
	$frame->drawimage($drawing);
	
	$frame->setImageDelay(10);
	
	$frame = new Gmagick();
	$frame->newimage( int $width , int $height , string $background [, string $format ] )
	
	
	$aniGif->addImage($frame);
}
*/

$colors = array(
	"Red",
	"Orange",
	"Yellow",
	"Green",
	"Indigo",
	"Violet",
);

foreach ($colors as $color) {
	$frame = new Gmagick();
	$frame->newimage(100, 100, $color);
	$aniGif->addImage($frame);
}

$aniGif->setImageFormat('gif');

//Even this fails.
//$aniGif->writeImage("./testgif.gif");

$aniGif = $aniGif->deconstructImages();
$bytes = $aniGif->getImagesBlob();
if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 

echo "Ok";
?>
--EXPECTF--
Frame: 0
Frame: 1
Frame: 2
Frame: 3
Frame: 4
Frame: 5
OkPK��\"5�@NN#gmagickpixel-006-setcolorcount.phptnu�[���--TEST--
Test getColorcount method
--SKIPIF--
<?php
/* $Id: gmagickpixel-003-getcolorcount.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$color = "rgb(255,255,255)";
$pixel = new GmagickPixel($color);
$pixel->setColorCount(2);
echo $pixel->getColorCount();
?>
--EXPECTF--
2PK��\C%E�ll&gmagickdraw_034_setClipRule_basic.phptnu�[���--TEST--
Test GmagickDraw, setClipRule
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function setClipRule($strokeColor, $fillColor, $backgroundColor) {

    $draw = new \GmagickDraw();

    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);
    $draw->setStrokeOpacity(1);
    $draw->setStrokeWidth(2);
    //\Gmagick::FILLRULE_EVENODD
    //\Gmagick::FILLRULE_NONZERO

    $clipPathName = 'testClipPath';
    $draw->pushClipPath($clipPathName);
    $draw->setClipRule(\Gmagick::FILLRULE_EVENODD);

    $draw->rectangle(0, 0, 300, 500);
    $draw->rectangle(200, 0, 500, 500);
    $draw->popClipPath();
    $draw->setClipPath($clipPathName);
    $draw->rectangle(200, 200, 300, 300);
    
    $clipRule = $draw->getClipRule();
    if ($clipRule !== \Gmagick::FILLRULE_EVENODD) {
        echo "Failed to get correct clipRule $clipRule != \Gmagick::FILLRULE_EVENODD \n";
    }

    $gmagick = new \Gmagick();
    $gmagick->newImage(500, 500, $backgroundColor);
    $gmagick->setImageFormat("png");

    $gmagick->drawImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

setClipRule($strokeColor, $fillColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\��E�gmagick-088-getimageblob.phptnu�[���--TEST--
getimageBlob test
--SKIPIF--
<?php
/* $Id: gmagick-086-setcompressionquality.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick("magick:rose");
$blob = $gm->getimageBlob();
echo "ok";
?>
--EXPECTF--
ok
PK��\�aƽ77$gmagick-031-set_getimagecompose.phptnu�[���--TEST--
Set and get image compose
--SKIPIF--
<?php
/* $Id: gmagick-031-set_getimagecompose.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageCompose(1)->getImageCompose();
?>
--EXPECTF--
1PK��\(��gmagick-078-rollimage.phptnu�[���--TEST--
rollImage test
--SKIPIF--
<?php
/* $Id: gmagick-078-rollimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->rollImage(5,5);
echo "ok";
?>
--EXPECTF--
okPK��\aGޯgmagick-085-stripimage.phptnu�[���--TEST--
stripImage test
--SKIPIF--
<?php
/* $Id: gmagick-085-stripimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->stripImage();
echo "ok";
?>
--EXPECTF--
okPK��\"��{MMgmagick-079-rotateimage.phptnu�[���--TEST--
rotateImage test
--SKIPIF--
<?php
/* $Id: gmagick-079-rotateimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$color=new GmagickPixel("rgb(255,255,255)");
$gm->rotateImage($color, 0.8);
echo "ok";
?>
--EXPECTF--
okPK��\Q�%G��:gmagickdraw-031-setdashstrokearray_getdashstrokearray.phptnu�[���--TEST--
Test set and get stroke dash array
--SKIPIF--
<?php
/* $Id: gmagickdraw-031-setdashstrokearray_getdashstrokearray.phpt 280206 2013-12-14 18:04:00Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
    $gDraw = new GmagickDraw();
    $gDraw->setStrokeDashArray(array(3, 2));
    print_r($gDraw->getStrokeDashArray());
?>
--EXPECT--
Array
(
    [0] => 3
    [1] => 2
)PK��\�T6���6gmagickdraw-013-setstrokecolor_getstrokecolor_q16.phptnu�[���--TEST--
Test setstrokecolor, getstrokecolor
--SKIPIF--
<?php
/* $Id: gmagickdraw-013-setstrokecolor_getstrokecolor.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH != 16) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$color = "rgb(255,255,255)";
$pixel = new GmagickPixel($color);
$gd = new GMagickDraw();
print_r($gd->setStrokeColor($pixel)->getStrokeColor()->getColor());
?>
--EXPECT--
rgb(65535,65535,65535)
PK��\3��))$gmagick-022-flip_and_flop_image.phptnu�[���--TEST--
Flip and flop image
--SKIPIF--
<?php
/* $Id: gmagick-022-flip_and_flop_image.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->flipImage()->flopImage();
echo "1";
?>
--EXPECTF--
1PK��\?*�� gmagick-089-getnumberimages.phptnu�[���--TEST--
getNumberImages test
--SKIPIF--
<?php
/* $Id: gmagick-086-setcompressionquality.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick("magick:rose");
print_r($gm->getNumberImages());
?>
--EXPECTF--
1
PK��\V6B��#gmagick-123_sharpenImage_basic.phptnu�[���--TEST--
Test Gmagick, sharpenImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$radius = 5;
$sigma = 1;

function sharpenImage($radius, $sigma) {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->sharpenimage($radius, $sigma);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

sharpenImage($radius, $sigma);
echo "Ok";
?>
--EXPECTF--
OkPK��\�A�Qkkgmagick-106-testConstants.phptnu�[���--TEST--
Test contants have symbols.
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$reflClass = new ReflectionClass('Gmagick');
// This just checks that the symbols that Gmagick was compiled with
// are actually present.
$constants = $reflClass->getConstants();

echo "ok";
?>
--EXPECTF--
okPK��\����"gmagickdraw_041_popDefs_basic.phptnu�[���--TEST--
Test GmagickDraw, popDefs
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function popDefs($strokeColor, $fillColor, $backgroundColor) {

    $draw = new \GmagickDraw();

    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);
    $draw->setstrokeOpacity(1);
    $draw->setStrokeWidth(2);
    $draw->setFontSize(72);
    $draw->pushDefs();
    $draw->setStrokeColor('white');
    $draw->rectangle(50, 50, 200, 200);
    $draw->popDefs();

    $draw->rectangle(300, 50, 450, 200);

    $gmagick = new \Gmagick();
    $gmagick->newImage(500, 500, $backgroundColor);
    $gmagick->setImageFormat("png");
    $gmagick->drawImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

popDefs($strokeColor, $fillColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\7��Y))gmagick-068-modulateimage.phptnu�[���--TEST--
modulateImage test
--SKIPIF--
<?php
/* $Id: gmagick-068-modulateimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->modulateImage(0.2, 0.2, 0.2);
echo "ok";
?>
--EXPECTF--
ok
PK��\�Mx���2gmagickdraw-006-setfillcolor_getfillcolor_q16.phptnu�[���--TEST--
Test setfillcolor, getfillcolor
--SKIPIF--
<?php
/* $Id: gmagickdraw-006-setfillcolor_getfillcolor.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH != 16) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$color = "rgb(255,255,255)";
$pixel = new GmagickPixel($color);
$gd = new GMagickDraw();
print_r($gd->setFillColor($pixel)->getFillColor()->getColor());
?>
--EXPECT--
rgb(65535,65535,65535)
PK��\3j$gmagick-117_haldClutImage_basic.phptnu�[���--TEST--
Test Gmagick, haldClutImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php


function haldClutImage() {
    $gmagick = new \Gmagick("magick:logo");
    $gmagickPalette = new \Gmagick(__DIR__."/hald_8.png");

    //$gmagickPalette->sepiatoneImage(55);
    $gmagick->haldClutImage($gmagickPalette);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

haldClutImage() ;
echo "Ok";
?>
--EXPECTF--
OkPK��\JPgmagick-058-getversion.phptnu�[���--TEST--
getVersion test
--SKIPIF--
<?php
/* $Id: gmagick-058-getversion.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->getVersion();
echo "ok";
?>
--EXPECTF--
ok
PK��\��V���bug_71742.phptnu�[���--TEST--
Bug #71742 polyline touched by array_walk
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc');
--FILE--
<?php

$coordinates = [];

foreach (range (0, 100) as $index) {
	$coordinates[] = array(
		'x' => 2 * $index,
		'y' => pow($index, 2)
	);
}

$callback = function (&$coordinate) {
	$coordinate['y'] = 200 - $coordinate['y'] / 50;
};

array_walk($coordinates, $callback);

$imagick = new GMagick();
$imagick->newImage(200, 200, "white");

$draw = new GmagickDraw();
$draw->setFillColor("none");
$draw->setStrokeColor("black");

//Fatal error in PHP 7, but not in PHP <= 5.6
$draw->polyline($coordinates);

$draw->translate(0, -20);
////Works in PHP 7
$draw->polyline (array_values($coordinates));
$imagick->drawImage($draw);
//$imagick->writeImage(getcwd(). "/test.png");
$imagick->setImageFormat('png');
$bytes = $imagick->getImageBlob();

if (strlen($bytes) <= 0) { 
	echo "Failed to generate image.";
}

//$imagick->writeImage("./bugTest.png");

echo "Ok";

?>
--EXPECT--
OkPK��\41�.��!gmagick-118_shaveImage_basic.phptnu�[���--TEST--
Test Gmagick, shaveImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php


function shaveImage() {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->shaveImage(100, 50);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

shaveImage() ;
echo "Ok";
?>
--EXPECTF--
OkPK��\*�@a$$,gmagickdraw-009-setfontsize_getfontsize.phptnu�[���--TEST--
Test setfontsize, getfontsize
--SKIPIF--
<?php
/* $Id: gmagickdraw-009-setfontsize_getfontsize.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gd = new GMagickDraw();
echo $gd->setFontSize(1)->getFontSize();
?>
--EXPECT--
1PK��\�\�yy(gmagick-026-set_getimageblueprimary.phptnu�[���--TEST--
Set, get imageblueprimary test
--SKIPIF--
<?php
/* $Id: gmagick-026-set_getimageblueprimary.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
print_r($gm->setImageBluePrimary(0.2, 0.2)->getImageBluePrimary());
?>
--EXPECTF--
Array
(
    [x] => 0.2
    [y] => 0.2
)PK��\���;00"gmagick-032-set_getimagedelay.phptnu�[���--TEST--
Set, getimagedelay test 
--SKIPIF--
<?php
/* $Id: gmagick-032-set_getimagedelay.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageDelay(2)->getImageDelay();
?>
--EXPECTF--
2PK��\ܣJ���gmagick-098-cloneimage.phptnu�[���--TEST--
cloneimage test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$cloned = $image->cloneimage();
echo "ok";
?>
--EXPECTF--
ok
PK��\ç|772gmagick-025-set_and_getimageckgroundcolor_q16.phptnu�[���--TEST--
Test setImageBackgroundColor and getImageBackgroundColor methods
--SKIPIF--
<?php
/* $Id: gmagick-025-set_and_getimageckgroundcolor.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH != 16) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$color=new GmagickPixel("rgb(255,255,255)");
$pixel =  $gm->setImageBackgroundColor($color)->getimagebackgroundcolor();
print_r($pixel->getColor());
?>
--EXPECTF--
rgb(65535,65535,65535)
PK��\�q��gmagick-059-hasnextimage.phptnu�[���--TEST--
hasNextImage test
--SKIPIF--
<?php
/* $Id: gmagick-059-hasnextimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->hasNextImage();
echo "ok";
?>
--EXPECTF--
ok
PK��\�Z؟�gmagick-006-annotateimage.phptnu�[���--TEST--
Test annotate image
--SKIPIF--
<?php
/* $Id: gmagick-006-annotateimage.phpt 333024 2014-03-17 05:37:54Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");

$fonts = $gm->queryFonts();

if (!empty($fonts))
    $gd->setFont($fonts[0]);

$gm->annotateImage($gd, 5, 15, 0, 'Test Text');
echo "ok";
?>
--EXPECT--
ok
PK��\̐�N��gmagickdraw-024-scale.phptnu�[���--TEST--
Test scale
--SKIPIF--
<?php
/* $Id: gmagickdraw-024-scale.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");
$gd->rectangle(20,20,30,30);
$gd->scale(0.5,0.5);
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\\Dϟ��2gmagickdraw-001-setstrokecolor_setstrokewidth.phptnu�[���--TEST--
Test set stroke color, set stroke width
--SKIPIF--
<?php
/* $Id: gmagickdraw-001-setstrokecolor_setstrokewidth.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$color = "rgb(255,255,123)";
$pixel = new GmagickPixel($color);
$gd = new GMagickDraw();
$gd->setStrokeColor($pixel)->setStrokeWidth(2);
echo "ok";
?>
--EXPECT--
okPK��\��@:DD$gmagickdraw-008-setfont_getfont.phptnu�[���--TEST--
Test setfont, getfont
--SKIPIF--
<?php
/* $Id: gmagickdraw-008-setfont_getfont.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gd = new GMagickDraw();
echo strstr($gd->setFont(dirname(__FILE__) . "/Vera.ttf")->getFont(),"Vera.ttf");
?>
--EXPECT--
Vera.ttfPK��\�����gmagickdraw-018-point.phptnu�[���--TEST--
Test point
--SKIPIF--
<?php
/* $Id: gmagickdraw-018-point.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");
$gd->point(20,20);
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\�k�]::*gmagickdraw-028-setlinecap_getlinecap.phptnu�[���--TEST--
Test set and get line cap
--SKIPIF--
<?php
/* $Id: gmagickdraw-028-setlinecap_getlinecap.phpt 280206 2013-11-05 12:46:00Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
	$gDraw = new GmagickDraw();
	$gDraw->setStrokeLineCap(2);
	echo $gDraw->getStrokeLineCap();
?>
--EXPECT--
2
PK��\|5�Vgmagick-030-getimagecolors.phptnu�[���--TEST--
Getimagecolors test
--SKIPIF--
<?php
/* $Id: gmagick-030-getimagecolors.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
print_r($gm->getImageColors());
?>
--EXPECTF--
3019PK��\O�O�&gmagick-125_radialBlurImage_basic.phptnu�[���--TEST--
Test Gmagick, radialBlurImage
--SKIPIF--
<?php

die("skip - not implemented by GraphicsMagick");

//checkClassMethods('Gmagick', array('radialBlurImage'));
?>
--FILE--
<?php


function radialBlurImage() {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->radialBlurImage(3);
    $gmagick->radialBlurImage(5);
    $gmagick->radialBlurImage(7);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

radialBlurImage() ;
echo "Ok";
?>
--EXPECTF--
OkPK��\�#�cc)gmagickdraw_038_setFontStretch_basic.phptnu�[���--TEST--
Test GmagickDraw, setFontStretch
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function setFontStretch($fillColor, $strokeColor, $backgroundColor) {

    $draw = new \GmagickDraw();

    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);
    $draw->setStrokeWidth(2);
    $draw->setFontSize(36);

    $fontStretchTypes = array(
        \Gmagick::STRETCH_ULTRACONDENSED,
        \Gmagick::STRETCH_CONDENSED,
        \Gmagick::STRETCH_SEMICONDENSED,
        \Gmagick::STRETCH_SEMIEXPANDED,
        \Gmagick::STRETCH_EXPANDED,
        \Gmagick::STRETCH_EXTRAEXPANDED,
        \Gmagick::STRETCH_ULTRAEXPANDED,
        \Gmagick::STRETCH_ANY
    );

    $offset = 0;
    foreach ($fontStretchTypes as $fontStretch) {
        $draw->setFontStretch($fontStretch);
        $draw->annotate(50, 75 + $offset, "Lorem Ipsum!");
        $offset += 50;
        
        $currentFontStretch = $draw->getFontStretch($fontStretch);

        if ($currentFontStretch !== $fontStretch) {
            echo "Failed to get correct fontStretch $currentFontStretch != $fontStretch \n";
        }
    }

    $gmagick = new \Gmagick();
    $gmagick->newImage(500, 500, $backgroundColor);
    $gmagick->setImageFormat("png");
    $gmagick->drawImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

setFontStretch($fillColor, $strokeColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\N� ++"gmagick-050-set_getimageunits.phptnu�[���--TEST--
Set, get imageunits
--SKIPIF--
<?php
/* $Id: gmagick-050-set_getimageunits.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageUnits(2)->getImageUnits();
?>
--EXPECTF--
2PK��\���ؠ�gmagickdraw-017-line.phptnu�[���--TEST--
Test line
--SKIPIF--
<?php
/* $Id: gmagickdraw-017-line.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");
$gd->line(8, 8, 18, 18);
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\��W++"gmagick-047-set_getimagescene.phptnu�[���--TEST--
Set, get imagescene
--SKIPIF--
<?php
/* $Id: gmagick-047-set_getimagescene.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageScene(1)->getImageScene();
?>
--EXPECTF--
1PK��\5[��~~gmagickdraw-020-polyline.phptnu�[���--TEST--
Test polyline
--SKIPIF--
<?php
/* $Id: gmagickdraw-020-polyline.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php

$intPoints = array(
    array( 'x' => 3, 'y' => 4 ), 
    array( 'x' => 2, 'y' => 6 ),
    array( 'x' => 5, 'y' => 10 ),
    array( 'x' => 3, 'y' => 4 )
);

$floatPoints = array(
    array( 'x' => 3.5, 'y' => 4.5 ), 
    array( 'x' => 2.5, 'y' => 6.5 ),
    array( 'x' => 5.5, 'y' => 10.5 ),
    array( 'x' => 3.5, 'y' => 4.5 )
);

$tests = array(
    $intPoints,
    $floatPoints
);

foreach ($tests as $polArray) {
    $filename = dirname(__FILE__) . '/test_constructor.jpg';
    $gm = new GMagick();
    $gd = new GMagickDraw();
    $gm->read("magick:rose");  
    $gd->polyline($polArray);
    $gm->drawImage($gd);
    $gm->write($filename);
    unlink($filename);
    echo "ok".PHP_EOL;
}
?>
--EXPECT--
ok
okPK��\����� gmagick-002-crop_variations.phptnu�[���--TEST--
Test crop with and without an image
--SKIPIF--
<?php
/* $Id: gmagick-002-crop_variations.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose")->cropImage(10, 10, 10, 10);
echo "ok\n--\n";
$gm = new Gmagick();
try {
	$gm->cropImage(10, 10, 10, 10);
} catch (GmagickException $e) {
	echo "got exception";
}
?>
--EXPECTF--
ok
--
got exceptionPK��\�
�%gmagick-004-composite_variations.phptnu�[���--TEST--
Test composite variations
--SKIPIF--
<?php
/* $Id: gmagick-004-composite_variations.phpt 294865 2010-02-10 23:50:11Z mkoppanen $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick("magick:rose");
$source = new Gmagick();

try {
	$gm->compositeImage($source, Gmagick::COMPOSITE_OVER, 0, 0);
} catch (Exception $e) {
	echo "got exception\n";
}

$source->read("magick:rose");
$gm->compositeImage($source, Gmagick::COMPOSITE_OVER, 0, 0);

echo "ok";
?>
--EXPECTF--
got exception
okPK��\%��pp'gmagick-051-set_getimagewhitepoint.phptnu�[���--TEST--
Set, get imagewhitepoint
--SKIPIF--
<?php
/* $Id: gmagick-051-set_getimagewhitepoint.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
print_r($gm->setImageWhitePoint(0.2, 0.2)->getImageWhitePoint());
?>
--EXPECTF--
Array
(
    [x] => 0.2
    [y] => 0.2
)PK��\Rȁgmagick-020-enhanceimage.phptnu�[���--TEST--
Enhance
--SKIPIF--
<?php
/* $Id: gmagick-020-enhanceimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->enhanceImage();
echo "ok";
?>
--EXPECTF--
okPK��\R��{{gmagick-007-blurimage.phptnu�[���--TEST--
Test blur image
--SKIPIF--
<?php
/* $Id: gmagick-007-blurimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gm->read("magick:rose");
$gm->blurImage(5, 3);
$gm->writeImage($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\v��gmagick-064-magnifyimage.phptnu�[���--TEST--
magnifyImage test
--SKIPIF--
<?php
/* $Id: gmagick-064-magnifyimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->magnifyImage();
echo "ok";
?>
--EXPECTF--
okPK��\ҿ�;;,gmagickdraw-029-setlinejoin_getlinejoin.phptnu�[���--TEST--
Test stroke line join
--SKIPIF--
<?php
/* $Id: gmagickdraw-0229-setlinejoin_getlinejoin.phpt 280206 2013-11-05 12:46:00Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
	$gDraw = new GmagickDraw();
	$gDraw->setStrokeLineJoin(2);
	echo $gDraw->getStrokeLineJoin();
?>
--EXPECT--
2
PK��\א
�gmagick-055-getreleasedate.phptnu�[���--TEST--
get releasedate
--SKIPIF--
<?php
/* $Id: gmagick-055-getreleasedate.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->getReleaseDate();
echo "ok";
?>
--EXPECTF--
okPK��\^&��	�	"gmagick-129_gettersAndSetters.phptnu�[���--TEST--
Test static methods
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$gmagick = new \Gmagick("magick:logo");






$gmagick->getImageVirtualPixelMethod();
$gmagick->getImageBackgroundColor();
$gmagick->getImageBluePrimary();
$gmagick->getImageBorderColor();
$gmagick->getImagesBlob();
$gmagick->getImageColorspace();
$gmagick->getImageCompose();
$gmagick->getImageDelay();
$gmagick->getImageDepth();
$gmagick->getImageCompression();
$gmagick->getImageDispose();
$gmagick->getImageExtrema();
$gmagick->getImageFilename();
$gmagick->getImageFormat();
$gmagick->getImageGamma();
$gmagick->getImageGreenPrimary();
$gmagick->getImageIndex();
$gmagick->getImageInterlaceScheme();
$gmagick->getImageIterations();
$gmagick->getImageMatteColor();

$gmagick->getImageRedPrimary();
$gmagick->getImageRenderingIntent();
$gmagick->getImageResolution();
$gmagick->getImageScene();
$gmagick->getImageBoundingbox(0.1);
$gmagick->getImageFuzz();
$gmagick->getImageSavedType();

$gmagick->getImageType();
$gmagick->getImageUnits();
$gmagick->getImageWhitepoint();

$gmagick->getSamplingfactors();
$gmagick->getSize();
$gmagick->getImageGeometry();
$gmagick->setImageAttribute("comment", "this is a comment");
$gmagick->setImageColormapColor(0, 'red');

$gmagick->setImageMatteColor('pink');
$gmagick->setDepth(16);
$gmagick->setImageFuzz(0.1 * \GMAGICK::QUANTUM);
$gmagick->setImageOption("jpeg", "preserve-settings", "true");
$gmagick->setImageSavedType(\GMAGICK::IMGTYPE_TRUECOLOR);
$gmagick->setResolutionUnits(\GMAGICK::RESOLUTION_PIXELSPERCENTIMETER);
$gmagick->setImageIndex(0);
$gmagick->setInterlaceScheme(\GMAGICK::INTERLACE_PLANE);
$gmagick->setImageCompression(\GMAGICK::COMPRESSION_NO);
$gmagick->setImageFormat("BMP");

$gmagick->setResourceLimit(\GMAGICK::RESOURCETYPE_MEMORY, 16*1024*1024);
$gmagick->getImageAttribute("comment");
$gmagick->getImageChannelExtrema(\GMAGICK::CHANNEL_RED);
$gmagick->getImageChannelMean(\GMAGICK::CHANNEL_RED);
$gmagick->getImageColormapColor(0);
$gmagick->getImageChannelDepth(\GMAGICK::CHANNEL_RED);
$gmagick->getResourceLimit(\GMAGICK::RESOURCETYPE_MEMORY);

if (method_exists($gmagick, 'setImageGravity')) {
	$gmagick->setImageGravity(\GMAGICK::GRAVITY_SOUTHWEST);
}

if (method_exists($gmagick, 'getImageGravity')) {
	$gmagick->getImageGravity();
}

if (method_exists($gmagick, 'getImagePage')) {
	$gmagick->getImagePage();
}


// Don't know how to call this successfully.
// $gmagick->get ImageProfile("ICC");


echo 'ok';

?>
--EXPECT--
okPK��\�Gjj*gmagickdraw-026-setgravity_getgravity.phptnu�[���--TEST--
Test affine
--SKIPIF--
<?php
/* $Id: gmagickdraw-025-affine.phpt 280206 2010-08-07 12:46:00Z vito $ */
require_once(dirname(__FILE__) . '/skipif.inc');

checkClassMethods(
    'GmagickDraw',
    array('setGravity', 'getGravity')
);
?>
--FILE--
<?php
    $gDraw = new GmagickDraw();
    $gDraw->setGravity(2);
	echo $gDraw->getGravity();
?>
--EXPECT--
2
PK��\��5xgmagick-018-edgeimage.phptnu�[���--TEST--
Edge
--SKIPIF--
<?php
/* $Id: gmagick-018-edgeimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->edgeImage(0.8);
echo "ok";
?>
--EXPECTF--
okPK��\���(gmagick-124__reduceNoiseImage_basic.phptnu�[���--TEST--
Test Gmagick, reduceNoiseImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
checkClassMethods('Gmagick', array('reduceNoiseImage'));
?>
--FILE--
<?php

$reduceNoise = 5;

function reduceNoiseImage($reduceNoise) {
    $gmagick = new \Gmagick("magick:logo");
    @$gmagick->reduceNoiseImage($reduceNoise);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

reduceNoiseImage($reduceNoise) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\���*++'gmagickpixel-001-setcolor_getcolor.phptnu�[���--TEST--
Test setColor and getColor methods
--SKIPIF--
<?php
/* $Id: gmagickpixel-001-setcolor_getcolor.phpt 331869 2013-10-20 11:27:00Z remi $ */
if(!extension_loaded('gmagick')) die('skip');
if (Gmagick::QUANTUM_DEPTH != 8) die('skip QUANTUM_DEPTH='.Gmagick::QUANTUM_DEPTH);
?>
--FILE--
<?php
$color = "rgb(255,255,255)";
$pixel = new GmagickPixel($color);
echo strcmp($color, $pixel->getColor()) . "\n";
var_dump($pixel->setColor($pixel->getColor()));
echo strcmp($color, $pixel->getColor()) . "\n";
?>
--EXPECTF--
0
object(GmagickPixel)#%d (%d) {
}
0
PK��\Z�|�� gmagick-054-getquantumdepth.phptnu�[���--TEST--
get quantumdepth
--SKIPIF--
<?php
/* $Id: gmagick-054-getquantumdepth.phpt 331865 2013-10-19 09:28:23Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$q=$gm->getQuantumDepth();
if (is_array($q) && count($q)==2 && isset($q['quantumDepthLong']) && isset($q['quantumDepthString'])) {
   echo "Struct OK\n";
   if ($q['quantumDepthLong']=='8' && $q['quantumDepthString']=='Q8') {
       echo "Quantum OK\n";
   } else if ($q['quantumDepthLong']=='16' && $q['quantumDepthString']=='Q16') {
       echo "Quantum OK\n";
   } else {
       echo "Quantum KO\n";
       print_r($q);
   }
} else {
   echo "Struct KO\n";
   print_r($q);
}
?>
--EXPECTF--
Struct OK
Quantum OKPK��\4�q���gmagick-128_staticMethods.phptnu�[���--TEST--
Test static methods
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$gmagick = new Gmagick ();

//echo gettype (Gmagick::queryFormats ()) . PHP_EOL;
echo gettype ($gmagick->queryFormats ()) . PHP_EOL;

//echo gettype (Gmagick::queryFonts ()) . PHP_EOL;
echo gettype ($gmagick->queryFonts ()) . PHP_EOL;

echo 'success';

?>
--EXPECT--
array
array
successPK��\^��gmagick-119_thumbnail_fill.phptnu�[���--TEST--
Test filling thumbnail with color
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc');

$v = Gmagick::getVersion();
if ($v['versionNumber'] < 0x632)
	die ('skip too old ImageMagick');

if ($v ['versionNumber'] >= 0x660 && $v ['versionNumber'] < 0x670)
	die ('skip seems to be broken in this version of ImageMagick');
?>
--FILE--
<?php

$gmagick = new Gmagick("magick:logo");
$gmagick->setImageBackgroundColor("pink");
$gmagick->thumbnailImage(200, 200, true);


echo "ok"
?>
--EXPECT--

okPK��\`��-llgmagick-012-chopimage.phptnu�[���--TEST--
Test chop image
--SKIPIF--
<?php
/* $Id: gmagick-012-chopimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm->read("magick:rose");
$gm->chopImage(10, 10, 0, 0);
$gm->write($filename);
echo "ok";
?>
--EXPECTF--
okPK��\�ĜH440gmagickdraw-011-setfontweight_getfontweight.phptnu�[���--TEST--
Test setfontweight, getfontweight
--SKIPIF--
<?php
/* $Id: gmagickdraw-011-setfontweight_getfontweight.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gd = new GMagickDraw();
echo $gd->setFontWeight(101)->getFontWeight();
?>
--EXPECT--
101PK��\��h���-gmagick-108_adaptiveThresholdImage_basic.phptnu�[���--TEST--
Test Gmagick, adaptiveThresholdImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc'); 
?>
--FILE--
<?php

$width = 50;
$height = 20;
$adaptiveOffset = 0.125;

function adaptiveThresholdImage($width, $height, $adaptiveOffset) {
    $gmagick = new \Gmagick('magick:rose');
    $adaptiveOffsetQuantum = intval($adaptiveOffset * \Gmagick::QUANTUM);
    $gmagick->adaptiveThresholdImage($width, $height, $adaptiveOffsetQuantum);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

adaptiveThresholdImage($width, $height, $adaptiveOffset) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\����--gmagick-008-addimage.phptnu�[���--TEST--
Test add image
--SKIPIF--
<?php
/* $Id: gmagick-008-addimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new GMagick();
$image = new GMagick();
$image->read("magick:rose");
$gm->addImage($image);
echo "ok";
?>
--EXPECT--
okPK��\�Vr��gmagickdraw-003-annotate.phptnu�[���--TEST--
Test annotate
--SKIPIF--
<?php
/* $Id: gmagickdraw-003-annotate.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");//->write($filename);
$gd->annotate(10, 20, "Test Text");
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\i@��+gmagick-109_affineTransformImage_basic.phptnu�[���--TEST--
Test Gmagick, affineTransformImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php


function affineTransformImage() {
    $gmagick = new \Gmagick("magick:logo");
    $draw = new \GmagickDraw();

    $angle = 40 ;

    $affineRotate = array(
        "sx" => cos($angle), "sy" => cos($angle), 
        "rx" => sin($angle), "ry" => -sin($angle), 
        "tx" => 0, "ty" => 0,
    );

    $draw->affine($affineRotate);

    $gmagick->affineTransformImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

affineTransformImage() ;
echo "Ok";
?>
--EXPECTF--
OkPK��\."&C**"gmagick-033-set_getimagedepth.phptnu�[���--TEST--
Set, getimagedepth
--SKIPIF--
<?php
/* $Id: gmagick-033-set_getimagedepth.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageDepth(2)->getImageDepth();
?>
--EXPECTF--
2PK��\��i��gmagick-095-negateimage.phptnu�[���--TEST--
negateImage test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->negateImage(TRUE);
echo "ok";
?>
--EXPECTF--
ok
PK��\K�3pp(gmagick-046-set_getimagerresolution.phptnu�[���--TEST--
Set, get imageresolution
--SKIPIF--
<?php
/* $Id: gmagick-046-set_getimagerresolution.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
print_r($gm->setImageResolution(0.2,0.2)->getImageResolution());
?>
--EXPECTF--
Array
(
    [x] => 0.2
    [y] => 0.2
)PK��\��
�44gmagick-009-addimagenoise.phptnu�[���--TEST--
Test addnoiseimage image
--SKIPIF--
<?php
/* $Id: gmagick-009-addimagenoise.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new GMagick();
$gm->read("magick:rose");
$gm->addNoiseImage(Gmagick::COLOR_BLACK);
echo "ok";
?>
--EXPECT--
okPK��\A�x�..%gmagickdraw_043_setViewBox_basic.phptnu�[���--TEST--
Test GmagickDraw, setViewBox
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';

function setViewBox($strokeColor, $fillColor, $backgroundColor) {

    $draw = new \GmagickDraw();

    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);
    $draw->setStrokeWidth(2);
    $draw->setFontSize(72);

    /*
     
    Sets the overall canvas size to be recorded with the drawing vector data. Usually this will be specified using the same size as the canvas image. When the vector data is saved to SVG or MVG formats, the viewbox is use to specify the size of the canvas image that a viewer will render the vector data on.
    
     */

    $draw->circle(250, 250, 250, 0);
    $draw->setviewbox(0, 0, 200, 200);
    $draw->circle(125, 250, 250, 250);
    $draw->translate(250, 125);
    $draw->circle(0, 0, 125, 0);


    $gmagick = new \Gmagick();
    $gmagick->newImage(500, 500, $backgroundColor);
    $gmagick->setImageFormat("png");

    $gmagick->drawImage($draw);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

setViewBox($strokeColor, $fillColor, $backgroundColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\�����$gmagick-115_equalizeImage_case2.phptnu�[���--TEST--
Test Gmagick, equalizeImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php


//This appears to corrupt the image colors?
function extentImage($startX, $startY, $width, $height) {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->equalizeImage();
    $gmagick->extentImage(
        $startX, $startY, $width, $height
    );
    
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}
$startX = 50;
$startY = 50;
$width = 150;
$height = 150;

extentImage($startX, $startY, $width, $height) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\]�y��gmagickpixel-004-clone.phptnu�[���--TEST--
Test object cloning and get
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$color = "rgb(255,255,255)";
$pixel = new GmagickPixel($color);
clone $pixel;
?>
--EXPECTF--
PK��\Yfe�gmagick-052-getimagewidth.phptnu�[���--TEST--
get imagewidth
--SKIPIF--
<?php
/* $Id: gmagick-052-getimagewidth.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->getImageWidth();
?>
--EXPECTF--
70PK��\��v:��gmagick-077-cropthumbnail.phptnu�[���--TEST--
cropthumbnail test
--SKIPIF--
<?php
/* $Id: gmagick-077-cropthumbnail.phpt 280549 2009-05-14 21:45:39Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->cropThumbnailImage(3, 3);

//Test the the calculated values are actually correct.
$desired_height = 250;
$imageWidth = 1128;

//Test the the calculated values are actually correct.
$desired_height = 250;
$imageWidth = 1128;
$imageHeight = 1128;

$legacySettings = array(0, 1);

foreach($legacySettings as $legacy) {
	for ($desired_width = 245; $desired_width < 255; $desired_width++) {
		$gmagick = new Gmagick();
		$gmagick->newImage ($imageWidth, $imageHeight, 'blue');

		$gmagick->cropThumbnailImage(
			$desired_width,
			$desired_height,
			$legacy
		);
		$error = false;

		$thumbnailImageWidth = $gmagick->getImageWidth();
		$thumbnailImageHeight = $gmagick->getImageHeight();

		if ($thumbnailImageHeight != $desired_height) {
			echo "Incorrect height for desired_width $desired_width imageHeight $imageHeight".PHP_EOL;
			$error = true;
		}

		$expectedWidth = $desired_width;
		$expectedHeight = $desired_height;

		if ($legacy == true && 
			$desired_width == 250 &&
			$desired_height == 250) {
			// The legacy behaviour is to generate a wrong size for this setting.
			// Thumbnail size of 249 x 250 does not matched desired size 250 x 250 
			// for source image of 1128 x 1128
			$expectedWidth = 249;
		}

		if ($thumbnailImageWidth != $expectedWidth) {
			echo "Unexpected width of $thumbnailImageWidth for desired_width $desired_width, expected width is $expectedWidth".PHP_EOL;
			$error = true;
		}

		if ($thumbnailImageHeight != $expectedHeight) {
			echo "Unexpected height $thumbnailImageHeight for desired_width $desired_width, expected height is $expectedHeight".PHP_EOL;
			$error = true;
		}

		if ($error) {
			printf(
				"Thumbnail size of %d x %d does not matched expected size %d x %d for source image of %d x %d. Legacy is %d\n",
				$thumbnailImageWidth, $thumbnailImageHeight,
				$desired_width, $desired_height,
				$imageWidth, $imageHeight,
				$legacy
			);
		}
	}
}

echo "ok";

?>
--EXPECTF--
ok
PK��\H6�Ɔ�!gmagickdraw-032_circle_basic.phptnu�[���--TEST--
Test GmagickDraw, circle
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$backgroundColor = 'rgb(225, 225, 225)';
$strokeColor = 'rgb(0, 0, 0)';
$fillColor = 'DodgerBlue2';
$originX = 250;
$originY = 250;
$endX = 400;
$endY = 400;

function circle($strokeColor, $fillColor, $backgroundColor, $originX, $originY, $endX, $endY) {

    //Create a GmagickDraw object to draw into.
    $draw = new \GmagickDraw();

    $strokeColor = new \GmagickPixel($strokeColor);
    $fillColor = new \GmagickPixel($fillColor);

    $draw->setStrokeOpacity(1);
    $draw->setStrokeColor($strokeColor);
    $draw->setFillColor($fillColor);

    $draw->setStrokeWidth(2);
    $draw->setFontSize(72);

    $draw->circle($originX, $originY, $endX, $endY);

    $imagick = new \Gmagick();
    $imagick->newImage(500, 500, $backgroundColor);
    $imagick->setImageFormat("png");
    $imagick->drawImage($draw);

    $bytes = $imagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

circle($strokeColor, $fillColor, $backgroundColor, $originX, $originY, $endX, $endY) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\��
ffgmagickdraw-002-ellipse.phptnu�[���--TEST--
Test ellipse
--SKIPIF--
<?php
/* $Id: gmagickdraw-002-ellipse.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$color = "rgb(255,255,255)";
$pixel = new GmagickPixel($color);
$gd = new GMagickDraw();
$gd->setFillColor($pixel)->ellipse( 200, 100, 50, 50, 0, 360 );
echo "ok";
?>
--EXPECT--
okPK��\1)d��+gmagick-107_setimagevirtualpixelmethod.phptnu�[���--TEST--
Test Tutorial, edgeExtend
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$virtualPixelType = 5;

function edgeExtend($virtualPixelType) {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->setImageVirtualPixelMethod($virtualPixelType);

    $gmagick->scaleimage(400, 300, true);

    $gmagick->setimagebackgroundcolor('pink');
   
    $desiredWidth = 600;
    $originalWidth = $gmagick->getImageWidth();

    //Make the image be the desired width.
    $gmagick->sampleimage($desiredWidth, $gmagick->getImageHeight());

    //Now scale, rotate, translate (aka affine project) it
    //to be how you want
    $points = array(//The x scaling factor is 0.5 when the desired width is double
        //the source width
        ($originalWidth / $desiredWidth), 0, //Don't scale vertically
        0, 1, //Offset the image so that it's in the centre
        ($desiredWidth - $originalWidth) / 2, 0);

    $amplitude = 5;
    $length = 20;
    $gmagick->waveImage($amplitude, $length);

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 

//Fyi it may be easier to think of the affine transform by 
//how it works for a rotation:
//$affineRotate = array(
//    "sx" => cos($angle),
//    "sy" => cos($angle),
//    "rx" => sin($angle),
//    "ry" => -sin($angle),
//    "tx" => 0,
//    "ty" => 0,
//);
}

edgeExtend($virtualPixelType) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\n� �� gmagick-116_waveImage_basic.phptnu�[���--TEST--
Test Gmagick, waveImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$amplitude = 5;
$length = 20;

function waveImage($amplitude, $length) {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->waveImage($amplitude, $length);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

waveImage($amplitude, $length) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\L��``"gmagick-048-getimagesignature.phptnu�[���--TEST--
Set, get imagesignature
--SKIPIF--
<?php
/* $Id: gmagick-048-getimagesignature.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->getImageSignature();
?>
--EXPECTF--
8b19185a62241bd7b79ecf3f619711f4ebbedd73eaeca0366f05778762b6614fPK��\W[�OOgmagick-082-shearimage.phptnu�[���--TEST--
shearImage test
--SKIPIF--
<?php
/* $Id: gmagick-082-shearimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$color=new GmagickPixel("rgb(255,255,255)");
$gm->read("magick:rose");
$gm->shearImage($color, 0.2, 0.2);
echo "ok";
?>
--EXPECTF--
okPK��\�4��MMgmagick-057-set_getsize.phptnu�[���--TEST--
set,get size test
--SKIPIF--
<?php
/* $Id: gmagick-057-set_getsize.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
print_r($gm->setSize(20, 20)->getSize());
?>
--EXPECTF--
Array
(
    [columns] => 20
    [rows] => 20
)PK��\�g��gmagick-094-thresholdimage.phptnu�[���--TEST--
thresholdImage test
--SKIPIF--
<?php
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->thresholdImage(2);
echo "ok";
?>
--EXPECTF--
ok
PK��\C� *gmagick-110_blackThresholdImage_basic.phptnu�[���--TEST--
Test Gmagick, blackThresholdImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$thresholdColor = 'rgb(127, 127, 127)';

function blackThresholdImage($thresholdColor) {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->blackthresholdimage($thresholdColor);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

blackThresholdImage($thresholdColor) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\��[55&gmagick-087-setcompressionquality.phptnu�[���--TEST--
setCompressionQuality test
--SKIPIF--
<?php
/* $Id: gmagick-086-setcompressionquality.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->setCompressionQuality(2);
echo "ok";
?>
--EXPECTF--
ok
PK��\k,����*gmagick-111_whiteThresholdImage_basic.phptnu�[���--TEST--
Test Gmagick, whiteThresholdImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$color = 'rgb(127, 127, 127)';

function whiteThresholdImage($color) {
    $gmagick = new \Gmagick("magick:logo");
    $gmagick->whiteThresholdImage($color);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

whiteThresholdImage($color) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\IZ��//gmagickdraw-019-polygon.phptnu�[���--TEST--
Test polygon
--SKIPIF--
<?php
/* $Id: gmagickdraw-019-polygon.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");
$polArray = array( array( 'x' => 3, 'y' => 4 ), array( 'x' => 2, 'y' => 6 ) ,array( 'x' => 5, 'y' => 10 ), array( 'x' => 3, 'y' => 4 ));
$gd->polygon($polArray);
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\�k��)�)
hald_8.pngnu�[����PNG


IHDR+���gAMA���a cHRMz&�����u0�`:�p��Q<bKGD������	X��(�IDATx���Kj��@����`�1G`�܍���Ո��xUPf��'��|[k�������O��?�����y���/��������v����o��_��ٿ��_k}���Q�o�R��[���o���k��^��U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ�t���]�#��`����[���V�o�_����Z@V���[�����/]k�O�Hշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K}���U�?���ߪ�U��/u�����[V�o�ߪ���� �d�������V���K�p=V���[��YͿU��R7\�U��V/V�o�ߪ��
�`�ߪ�U�0����/���jY���o����ߪ��
�`����[���V�o�_�������V��j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V�������Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿU��pշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K��V�`��j�XͿU��V�_��kY��:��ߪ�U��/�+@\Ȫ��`5�V���[����z��[��z����V��n���V��^��ߪ�U�����U��V/`V�o�_�;\����o��j�[ͿU���jY���o����ߪ�4���q��[����j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V����Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿUi��
�z��[m����o��j�K�p- ��V��[���o���~�kY��:��ߪ�U��R7\�U��V/`V�o�ߪ��
�`�ߪ����[���/u��X���o����j���K}���Z@V���[�����/u�uX- ��V�����[���;[�3NV}��VYͿU��V�_��kY��:��ߪ�U��/�+@\Ȫ��`5�V���[����z��[��z����V��n���V��^��ߪ�U�����U��V/`V�o�_�;\����o��j�[ͿU���jY���o����ߪ��w��[����j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V����Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/�wZAP�U��c5�V���[���d�������V����ԯq- ��V��[���o�_�����o����j���[����z��[��z����V��n���V���[��YͿU��pVȪ�U��o5�V��n��d�ߪ������ҵ����g������@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/]k�O�Hշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K}���U�?���ߪ�U��/u�����[V�o�ߪ���� �d�������V���K�p=V���[��YͿU��R7\�U��V/V�o�ߪ��
�`�ߪ�U�0����/���jY���o����ߪ��
�`����[���V�o�_�������V��j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V�������Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿU��pշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K}���U�?���ߪ�U��/u�����[V�o�ߪ���� �d�������V���K�p=V���[��YͿU��R7\�U��V/V�o�ߪ��
�`�ߪ�U�0����/���jY���o����ߪ��
�`����[���V�o�_�������V��j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V�������Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿU��pշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K��V�`��j�XͿU��V�_��kY��:��ߪ�U��/�+@\Ȫ��`5�V���[����z��[��z����V��n���V��^��ߪ�U�����U��V/`V�o�_�;\����o��j�[ͿU���jY���o����ߪ�4���q��[����j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V����Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿU�Z�~^� �����@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/]k�O�Hշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K}���U�?���ߪ�U��/u�����[V�o�ߪ���� �d�������V���K�p=V���[��YͿU��R7\�U��V/V�o�ߪ��
�`�ߪ�U�0����/���jY���o����ߪ��
�`����[���V�o�_�;\���[�d5�V���[���d�������V����ԯq- ��V��[���o�_�����o����j���[����z��[��z����V��n���V���[��YͿU��pVȪ�U��o5�V��n��d�ߪ�������|��X���>V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/�w��g���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿU��pշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K}���U�?���ߪ�U��/u�����[V�o�ߪ���� �d�������V���K�p=V���[��YͿU��R7\�U��V/V�o�ߪ��
�`�ߪ�U�0����/���jY���o����ߪ��
�`����[���V�o�_�������V��j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V�������Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿU��pշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K}���U�?���ߪ�U��/u�����[V�o�ߪ���� �d�������V���K�p=V���[��YͿU��R7\�U��V/V�o�ߪ��
�`�ߪ�U�0����/���jY���o����ߪ��
�`����[���V�o�_�������V��j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V�������Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿU�Z�~^[�3N�`��� ����o��j�K�p- ��V��[���o���~�kY��:��ߪ�U��R7\�U��V/`V�o�ߪ��
�`�ߪ����[���/u��X���o����j���K}���Z@V���[�����/u�uX- ��V�����[�����$�X���>V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/�w��g���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿU��pշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K}���U�?���ߪ�U��/u�����[V�o�ߪ���� �d�������V���K�p=V���[��YͿU��R7\�U��V/V�o�ߪ��
�`�ߪ�U�0����/���jY���o����ߪ��
�`����[���V�o�_�������V��j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V�������Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿU��pշ�o���[���o���n��U��j���[���R�ĵ���[V�o�ߪ�U�����U�0����o�_�����o����j���[����z��[���o�f5�V����uX- ��V�����[����:��U��V��j���K��V�`��j�XͿU��V�_��kY��:��ߪ�U��/�+@\Ȫ��`5�V���[����z��[��z����V��n���V��^��ߪ�U�����U��V/`V�o�_�;\����o��j�[ͿU���jY���o����ߪ�4���q��[����j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V����Uߪ��@V�o�ߪ�����Z@V������o��j�K�
���ouXͿU��V��n���V��^���ߪ�U�����U�����o�_�����o�ߪ����[����`����[���V�o�_����Z@V���[�����/���V�������V�����
���ouXͿU��V�_�W���U��j���[���/u��X���o�f5�V���K�p=V���[�XͿU��R7\�U��V��^���ߪ��w��d�ߪ������R7\����o��j�[ͿUi��
�z��[m����o��j�K�p- ��V��[���o���~�kY��:��ߪ�U��R7\�U��V/`V�o�ߪ��
�`�ߪ����[���/u��X���o����j���K}���Z@V���[�����/u�uX- ��V�����[���;[�3NV}��VYͿU��V�_��kY��:��ߪ�U��/�+@\Ȫ��`5�V���[����z��[��z����V��n���V��^��ߪ�U�����U��V/`V�o�_�;\����o��j�[ͿU���jY���o����ߪ��w��[����j���[���R7\Ȫ��`5�V���[��_�Z@V������o�ߪ��
�`�ߪ����[���/u��X���o�`5�V���K�p=V���[��z����R��:��U��V��j���K�pVȪ�U��o5�V����܏�VC�
%tEXtdate:create2014-05-31T13:00:02+00:00R��%tEXtdate:modify2014-05-31T13:00:02+00:00#��MIEND�B`�PK��\�ȉ�$gmagick-127_contrastImage_basic.phptnu�[���--TEST--
Test Gmagick, contrastImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$contrastType = 1;

function contrastImage($contrastType) {
    $gmagick = new \Gmagick("magick:logo");
    if ($contrastType != 2) {
        $gmagick->contrastImage($contrastType);
    }

    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

contrastImage($contrastType) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\��d��gmagickdraw-022-rotate.phptnu�[���--TEST--
Test rotate
--SKIPIF--
<?php
/* $Id: gmagickdraw-022-rotate.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new GMagick();
$gd = new GMagickDraw();
$gm->read("magick:rose");
$gd->rectangle(20,20,30,30);
$gd->rotate(45);
$gm->drawImage($gd);
$gm->write($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
okPK��\�t�M��1gmagickpixel-002-setcolorvalue_getcolorvalue.phptnu�[���--TEST--
Test setColorValue and getColorValue methods
--SKIPIF--
<?php
/* $Id: gmagickpixel-002-setcolorvalue_getcolorvalue.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gp = new GMagickPixel();
echo $gp->getColorValue(Gmagick::COLOR_BLACK). "\n";
$gp->setColorValue(Gmagick::COLOR_BLACK,1);
echo $gp->getColorValue(Gmagick::COLOR_BLACK). "\n";
?>
--EXPECTF--
0
1PK��\�\�gSS,gmagick-045-set_getimagerenderingintent.phptnu�[���--TEST--
Set, get imagerenderingintent
--SKIPIF--
<?php
/* $Id: gmagick-045-set_getimagerenderingintent.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
echo $gm->setImageRenderingIntent(1)->getImageRenderingIntent();
?>
--EXPECTF--
1PK��\I*�ff$gmagick-112_colorizeImage_basic.phptnu�[���--TEST--
Test Gmagick, colorizeImage
--SKIPIF--
<?php
$imageMagickRequiredVersion=0x675;
require_once(dirname(__FILE__) . '/skipif.inc');
?>
--FILE--
<?php

$color = 'rgb(127, 127, 127)';
$opacity = 100;

function colorizeImage($color, $opacity) {
    $gmagick = new \Gmagick("magick:logo");
    $opacity = $opacity / 255.0;
    $opacityColor = new \GmagickPixel("rgba(0, 0, 0, $opacity)");
    $gmagick->colorizeImage($color, $opacityColor);
    $bytes = $gmagick->getImageBlob();
    if (strlen($bytes) <= 0) { echo "Failed to generate image.";} 
}

colorizeImage($color, $opacity) ;
echo "Ok";
?>
--EXPECTF--
OkPK��\ל��kkgmagick-001-read_and_write.phptnu�[���--TEST--
Test read/write
--SKIPIF--
<?php
/* $Id: gmagick-001-read_and_write.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$filename = dirname(__FILE__) . '/test_constructor.jpg';
$gm = new Gmagick();
$gm->readImage("magick:rose")->writeImage($filename);
unlink($filename);
echo "ok";
?>
--EXPECT--
ok
PK��\{+�_��gmagick-120_writeImageFile.phptnu�[���--TEST--
Test PHP writing to php://memory 
--SKIPIF--
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
--FILE--
<?php

$gmagick = new Gmagick("magick:logo");
$gmagick->setFormat("png");

$fp = fopen("php://memory", 'r+');
$gmagick->writeImageFile($fp);
rewind($fp);
$memoryBlob = stream_get_contents($fp);
fclose($fp);

//This test depends on getImageBlob working correctly.
$imageBlob = $gmagick->getImageBlob();

//Read the images from the data blobs.
$imageReopened = new Gmagick();
$imageReopened->readImageBlob($imageBlob);
$memoryReopened = new Gmagick();
$memoryReopened->readImageBlob($memoryBlob);

//Compare to see if they are identical.
$result = $imageReopened->compareImages($memoryReopened, \Gmagick::METRIC_MEANABSOLUTEERROR);

if ($result[1] == 0) {
    echo "Reopened images are identical.";
}
else {
    echo "Error, reopened images have changed.";
    var_dump($result);
}

?>
--EXPECTF--
Reopened images are identical.
PK��\����YYVera.ttfnu�[���OS/2�_�c�pVPCLTъ^���6cmap����lXcvt ��9��fpgm���&`�gaspHglyftA�&��~hdmx4�!�Hhead݄��T6hheaEo�L$hmtx	Ǝ���0kern�Rՙ��-�loca���=��maxpG:�, nameټȵ�post�Z/����prep;� �h::_::dM0�l	�
	p	t	�	&
	

Y	&
	&
	
c	.
5	`
�	s	0�	
&
{Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc.
All Rights Reserved.
Bitstream Vera is a trademark of Bitstream, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:

The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.

The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera".

This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names.

The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.

THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.comCopyright (c) 2003 by Bitstream, Inc. All Rights Reserved.Bitstream Vera SansBitstreamVeraSans-RomanRelease 1.10Copyright (c) 2003 by Bitstream, Inc.

All Rights Reserved.

Bitstream Vera is a trademark of Bitstream, Inc.



Permission is hereby granted, free of charge, to any person obtaining a copy of the fonts accompanying this license ("Fonts") and associated documentation files (the "Font Software"), to reproduce and distribute the Font Software, including without limitation the rights to use, copy, merge, publish, distribute, and/or sell copies of the Font Software, and to permit persons to whom the Font Software is furnished to do so, subject to the following conditions:



The above copyright and trademark notices and this permission notice shall be included in all copies of one or more of the Font Software typefaces.



The Font Software may be modified, altered, or added to, and in particular the designs of glyphs or characters in the Fonts may be modified and additional glyphs or characters may be added to the Fonts, only if the fonts are renamed to names not containing either the words "Bitstream" or the word "Vera".



This License becomes null and void to the extent applicable to Fonts or Font Software that has been modified and is distributed under the "Bitstream Vera" names.



The Font Software may be sold as part of a larger software package but no copy of one or more of the Font Software typefaces may be sold by itself.



THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.



Except as contained in this notice, the names of Gnome, the Gnome Foundation, and Bitstream Inc., shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Font Software without prior written authorization from the Gnome Foundation or Bitstream Inc., respectively. For further information, contact: fonts at gnome dot org.http://www.bitstream.com5��������fq����u����-��������J3���T��99N�R���7s�`s3�V�V9����s��3�D��������{�o{R�����o���������H������T3f������s�
�+���b�-����{T��#���������\q��#�H�99`���#fy```{�w`��`b{�{�R�f�fw�;���{�J/��}oo5jo{��-��{��T7����f����D)f�s��@�����%�2�������%���%�A�������:�B��2��S��A�S��/��/���2�ݖ���}ٻ�֊A�}��G�}�G��������͖�����2�ƅ����������������������%���������]������%�]@��@�%�������������d���%�d���%���A��������������2��d��A������������d�����
����
���
��A�����(����������]������%�]�@�%����.���.��%�A������%�d���%�������������@�~}}~�}}|d{T{%z�y�xwv
u�t�s�r�q�p�o�n�l!k�jBjSi�h}gBf�e�d�c�b�a:`�^]�[�Z�YX
Y�X
WW2V�UTUBTSSRQJQ�PO�NMN�ML�KJK�JIJI
IH
G�F�E�D�C-C�B�AK@�?�>=>=<=<;
<@�;
:�9�878�767656543
212�10/0
/.-	.-	,2+*%+d*)*%)('%(A'%&%&%$�#�"!! d�
d
B��BB�d����B�-B}d�

��
	�-�d�@-�-��d��++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++�, �%Id�@QX �Y!-,�%Id�@QX �Y!-,  �P�
y ���PXY��%�%#� �P�
y ���PXY��%�-,KPX ��EDY!-,�%E`D-,KSX�%�%EDY!!-,ED-f��f�@��/���1����0!%!!f�s����r)5�	@@��
�<�2991/��0K�TX�
@

��878Y� P]%3#3#5�������q��e����M@������1�<�20K�TK�T[X�@��878Y@0	@	P	`	p	�	�	]#!#o�$���+��+��`@1�	
�
	
 ��91/<�<<�<<�<<�2�220@
]!!!3!!!!#!#!5!!5!��T%Dh$i�g8��R>��h�g��g�h��`T��if�����a��a�����b��b��N�����m!(/�@U"

'&(
/)/))/B"
)	*!��#��*��-
)	"	&
0�<��<�<���1/������2���99990KSX��9�9�Y"K�	TX�0@00��878YK�TK�T[K�T[X�0��00@878Y#.'5.546753.'>54&�di�jf�o����d]�SS�\����dtzq��{��---�@A�$�������*.�U#�����
jXV`�OnZXhq��)�#'3�@6$%&%&'$'B��.��$��&�($�4'!%	
	
!
+
14�������991�2�<�����0KSX��Y"K�	TK�T[K�T[K�T[K�T[K�
T[X�4@44��878Y"32654&'2#"&546"32654&%3#2#"&546�WccWUccU���������VcbWWcd1��Z�����������������ܻ��ۻ��a����������
ۻ��ۼ�������	0�@�
��
	�	���	 !


	




B	
(('�+�'�$��
.	.'.'!!1��������99999991/������9990KSX��9�9�9�9�9��9�9���9Y"�2]@�"	)**&:4D^YZ
UZZY0g{��������"�-		'(	2'')	#**(/2;	49?2J	LKFO2VZ	YUY\_2ji`2uyz��������	���2�2�29]]3267	>73#'#"5467.54632.#"�[UԠ_�I�{�;B�h]��h��Ά�02޸S�UW�Di�;#Q�X��?@���Y�r���~��YW׀�c?}<��$$�/1oX3g��o�B@
����1��0K�TK�T[X�@��878Y@
@P`p��]#o���+���{
O@��

��2�991��0K�TX�@��878YK�TX���@878Y#&547{����������>��;����������o
@���<��991��03#654�����������<��:������=J��N@,
	��
�
		
�<�2�<�2991��<�2��2990%#'%%73%��g:��r��:g��:PrP���b��y�b��c�y�����#@	�
�<�<�1/�<�<�0!!#!5!�-�Ө��-�Ӫ��-�-����@������1��073#�Ӥ�R����@d������1��0!!d�����������1/�073#�����B��-@B��/�991��0KSX��Y"3#�����m�����#@����	����1����0"32'2#"����������	������	P�����3343��s�����s�yz��Z�
K@B����	����1/�2����0KSXY"K�TX���@878Y�]7!5%3!!�J��e�J���sH�H�ժ�J��@'B��
���

����91/�2����0KSX��9Y"K�TK�T[K�T[X�@��878Y@2UVVzzv�tvust�������]]%!!567>54&#"5>32���Ls�3aM��_�xz�X�E[��w�:m�Iw�BC�12��\�p�����s�({@.
�	� �
�	�� �#���)& 	)�������991����������90K�TK�T[X�)@))��878Y@	daa d!]!"&'532654&+532654&#"5>32?�����^�jT�m�ǹ�������S�rs�Y��%Đ��%%�12�����wps{$&�  Ѳ|�d��
�@


B��	
��<��291/��<�290KSX��Y"K�TK�
T[X�@��878Y@**HYiw�+&+6NOO
Vfuz�
]]	!33##!5��5����^%���3���`����d�u@#����
��
��

������1���������90K�TK�T[X�@��878YK�TX���@878Y!!>32!"&'532654&#"���,X,�$���^�hZ�k��ʭQ�Tժ������  �10����$&�����$X@$
������"��%"	!%������1�������90@���������]]"32654&.#">32# !2��������	L�L��;�k������PL�;��������y�$&���W]������yb��h�c@B�����991/��0KSX��Y"K�TX�@��878Y@X9Hg��]]!#!�������3�V��+�����#/C@%�'�-���'�0$*$	!0��������991������990"32654&%&&54632#"$54632654&#"��������������������������H��������Ś�������V ����г�� "Ə���ُ�at��tt�������$X@#��
������%!""
%������1������90@����������]]7532#"543 !"&2654&#"�L�K��:�l������L�>��������$&
V\���s�����[�����������#@����<�21/���073#3#������#����#	%@����

�<�2��1���03#3#���Ӥ�R#��٬��@�^��M@*����B��$#��291��90KSX����Y"	5�������Ѧ��`��@
��#�<�21����0!!!!���������^��O@+����B��$#�<�91��90KSX����Y"55����/��/�m���$p@+$	
�����
	

&%������99991/����9990K�TX�%@%%��878Y�y	z
z ]%3##546?>54&#"5>32���ſ8ZZ93�lO�a^�g��HZX/'����e�VY5^1YnFC�98ŸL�VV/5<4���q�L�@2	��L43�0�7�$�7CM34((+(I+*(I,=M������<��991���������2��990K�	TK�T[K�T[K�T[K�T[X�M��MM@878Y@	NN/N?N]32654&#"#"&5463253>54&'&$#"3267#"$'&5476$32��|{��zy�!<�g��ثg�;���?@h�հ{�`��smi���hZ}�٘�������~�R��k{KO�����������HMI���KL�� ߱k�P��A@f������jhmWQoag�}}I��J}���b�{����h�
�@A


	


B��			
��91/<���90KSX��������Y"� ]@:Xvp�	VXPghxv|rwx������]]	!3#!#���%�{�9҈�_�����+����� C@#�
	���
�.	!��2����9991/�����90�"]!2654&#!2654&#%!2#!�D������+������|������݇���f�>orqp����� ˘��s��'�6@
��
������
0��2�1�������0�].# !267# !2'f�����fj���z�S��b�_^�����^_�HH�gh�G���.@�	��

2	����99991/���0�`]3 !%! )��5���B����h�P�a/�w.,������~�����.@�����
	��2���1/�����0�
]!!!!!!����9��>ժ�F����#�	)@����
��2��1/����0�]!!!!#�Z�pP���ժ�H��7s����9@ �������43����1��������990%!5!# !2&&# !26��u����u�^�op�����k�����SU�mn�HF�_`�����%�;�,@���
8	��2��21/<�2��0�P
]3!3#!#�����"���d�+�9���9����1/�0K�TX���@878Y@
0@P`��]3#����+���f��M@���9���991��990K�TX���@878Y@
0
@
P
`
�
�
]3+53265����M?�n�������j�
�@(B�	��2��91/<�290KSX����Y"�]@�	((764GFCUgvw������		

(+*66650A@E@@@b`hgwp���������������������,]q]q3!	!#�������3���w�H���1�j�%@��:���1/��0@	0P��]3!!���_�ժ���@4	
	

	B
�	
>

���91/<��290KSX����Y"�p]@V	

&& &
 
45
i|{y
���
�#,'(	4<VY	ej	vy	����]]!	!###�-}-������+����3�	y@B�6
���991/<�2990KSX��Y"�]@068HGif�	FIWXeiy�����]]!3!#����j���+�s����#@����	3���1����0"32' ! '�������:x������yL�����HH��[�����[�bb����:@��	�

?	��2��91/����0@?_�]32654&#%!2+#�������8������/�ϒ��������s����R@*


B����

	3���9991�����990KSX��9Y""32#'# ! '������?
��!#���y;:x�L�����HH�����ab��[�������T��@5	
B�	�
�	
?
��2���99991/<����9990KSX��9Y"�@]@Bz%%%&'&&&	 66FFhuuw����]]#.+#! 32654&#�A{>�ٿJ�x��������������~�h�b����؍�O��������'~@<
	
B
�����%��(
"-"(�����99991�������90KSX�9�9Y"�)]�)/)O)].#"!"&'532654&/.54$32Hs�_��w�z����j�{�r����{���i���76�vce+ٶ��0/�EF�~n|-����&����J@��@@���1/��20K�
TX�@��878Y@		@	p	�		]!!#!����ժ��+���)�K@��	�
8A���1�2��99990K�TX�@��878Y���]332653! �ˮ�®������u������\���*$h��@'B���91/�290KSX����Y"�P]@b*GGZ}�	*&&))% 833<<7HEEIIGYVfiizvvyyu���)]]!3	3J���������+D��{@I	
	
	


B
�
	
��91/<�2290KSX��������Y"�]@�



($
 
>>4
0
LMB
@
Yjkg
`
{|�
��			

 !   #	$
%  <:5306	9?0FFJ@E@BBB@@	D
M@@XVYPfgab```d	d
dwv{xwtyywpx�����	�����[]]3	3	3#	#D�:9�:9���������+��=;�]@F	


	B�	
����91/<�290KSX��������Y"K�TK�
T[K�T[X���@878Y@�''486KX[fkww����������			
&()&('	) 
54<;:;4	4
8?
H	O
X_
eejjhiil	l
xyyx}	
x��������
��������
�
@]]3	3	#	#��su� �\�Y���+�3��{�������@(B�@@	���91/�290KSX����Y"�
]@<5000F@@@QQQe��
&)78@
ghxp
�

]]3	3#��������f��9�\�	�@B���B
����991/���0KSX��Y"K�	TK�
T[X�
@

��878Y@@
)&8HGH	/59?GJOUYfiowx�]]!!!5!s��P�=��g՚�o������XS@����C��21����0K�TX���@878YK�TK�T[X�@��878Y!#3!����X�����B��-@B��/�991��0KSX��Y"#�����m���o<@����C�<��1����0K�TK�T[X���@878Y!53#5o�X���ޏ�����@
���91��290##��H�H�����u-��������1��0!5��������f1@	��D��1��0K�	TK�T[X���@878Y	#o���f��v{��-{
%�@'	�� ���#��
	E&�����22991/��������9990@n0000 0!0"?'@@@@ @!@"PPPP P!P"P'p'���� �!�"�'�'�'000 0!@@@ @!PPP P!``` `!ppp p!��� �!]]"326=7#5#"&5463!54&#"5>32�߬�o����?�������`�Te�Z��3f{bsٴ)L���fa�����..�''�����8@�	����GF��22��1/�������0�`��]4&#"326>32#"&'#3姒��������:�{���{�:��/�������Rda��������ad�q���{?@���
�
����
HE��2�1��������0@���].#"3267#"!2�N�P��ƳP�NM�]���-U�5�++����++�$$>:#q��Z8@�����GE����221/�������0�`��]3#5#"3232654&#"���:�|���|��ǧ��������^��daDDa��������q��{p@$	��������KE�����91��������90@)?p���?????,//	,
ooooo	]q]!3267# 32.#"��ͷj�bc�k���)�������^Z��44�*,8
C����/�p@����
	
L�<�<��991/�2���2990K�
TX���@878YK�TX�@��878Y�@P�]#"!!##535463��cM/�ѹ�����Phc��/яN��q�VZ{(J@#	���&#�'�	��&G E)�����221/����������990�`*�*�*]4&#"326!"&'5326=#"3253�������������a�QQ�R��9�|���|�9�=�����������,*��[cb::bc��d4@	���
N
	F��2��1/<����90�`]#4&#"#3>32d�||����B�u����\���������ed��y+@���F�<�21/��0@	@	P	`	p	]3#3#�����`�����VyD@�����O
F�<�2�991�����990@@P`p]3+532653#����F1iL��`����a�(���
�@)B��	F��2��91/<��90KSX����Y"�]@_
')+Vfgsw�������		
('(++@h`����������������]q]33	##��%�k�ǹ�i��#���y"��F��1/�0@
@P`p�]3#������{"Z@&	� ��PPF#��2����91/<<��<��290@0$P$p$�$�$�$�$�$�$	]>32#4&#"#4&#"#3>32)E�����ru���rw����?�yz��|v��\��������������`�gb|�d{6@	���
N
	F��2��1/<����90�`�]#4&#"#3>32d�||����B�u����\�������`�ed�q��u{J@����	QE����1����0@#?{{	
{
{��]"32654&'2#"s���������������������������98��V�{>@������GF��22��1��������0@	`���]%#3>32#"&4&#"326s��:�{���{�8�����������
�da��������a��������q�VZ{>@�	�����GE����221��������0@	`���]32654&#"#"3253#/��������s:�|���|�:��/�������daDDad����J{0@��	�
F���21/������90�P�].#"#3>32JI,����:��.�˾��`�fco���{'�@<
S	
SB
������%��(
R"E(������99991��������90KSX�9�9Y"�']@m
.	,
,,;	;
;;  $(
(*//*(() )!$'�
���


	'/)?)_))�)�)�)�)]]q.#"#"&'532654&/.54632�N�Z��b�?ĥ��Z�lf�a��e�@����f�?�((TT@I!*����##�55YQKP%$����7��8@���
	F�<�<�2991/��<��2990��]!!;#"&5#53w{��Ks��բ�������N���`�>���X`6@	��
�
	NF����21/�2���90�`�]332653#5#"&��||����C�u�����a����{���fc�=`@'B���91/�290KSX����Y"K�
TX���@878YK�TK�T[X�@��878Y@�Hj{����		&&)) 55::0FFIIFH@VVYYPffiigh`ut{{uz�������������������>]]3	3#=�^^�\�`�T���V5`@IU	
	U
	U

U
B
�
	
��91/<�2290KSX��������Y"K�
TK�T[K�T[K�T[K�T[X�
��

@878YK�TK�
T[K�T[X�
@

��878Y@�"
5
IIF
@
[[U
P
nnf
yy����
�����	
		%%#'!%""%'	$
!#96690FHF@B@@@D	D
D@@VVVPQRRPS	T
Ucdejejjjn	agouuy}x}zzxy		{
v}������������@/������������������	�����y]]333##V�����������`��j��j����j;y`Z@F
	


	B
�
����91/<�290KSX��������Y"K�
TK�T[K�T[K�T[X���@878YK�TX�@��878Y@�


&
=1
UWX
f
vzvt
��
����
�
�����
�

	
	)&%	* 
:9746	90
IFE	J@
YVYYWVYVV	YP
o
x
�����
�
�
�
/]]	#	#	3	d�k��������r�))`�����HJ�q�=�V`�@C	







B
	���

	���91�2��9990KSX������2Y"K�
TK�T[X���@878YK�TX�@��878Y@�	



#
5
I
O
N
Z	Z
j
�
�
�


	
'$$  )(	%
$$'
** 755008
668
990A@@@@@@@@B	E
G
II@TQQUPPVUVW	W
UUYYPffh
ii`{xx��	���
���	������������e]]+5326?3	3�N�|�lLT3!�;�^^�h�z�H�TN��lX�`	�@B���
��2�991/���0KSX��Y"K�TK�T[X�
@

��878YK�TX�
��

@878Y@B&GI+ 690@@E@@CWY_``f``b��]]!!!5!qj�L��}��e`��ۓ�%��$�@4%	 !�	��	��%	
$
 C
%�<�<�299999991������99999990K�TX�%��%%@878Y�&]#"&=4&+5326=46;#"3>��l�==�k��>D�V[noZV������t�s��ݓ�X��������X������1��0#�����$�@6% ��#���%#C%�<�2�<�99999991������99999990K�
TX�%@%%��878YK�TX�%��%%@878Y�&]326=467.=4&+532;#"+F�UZooZU�F?��l�>>�l��?�V��������W����s�t��ݔ���1#@����1����990#"'&'&'&#"56632326�i�an��^X�bi�an�
�^V�1�OD;>MS�OE<>L��hN'$�uhm!�@T

!!! !!!B
	�	�
	�  !
VV!"����2��299999991/<�����9990KSX��������Y"� #]@ s�P#f
iu
{yyv v!�#]]4&#"326!.54632#!#TY?@WX??Y���!�X=>�sr�?<҈�_��Z?YWA?XX��N)sIs��rFv)������s�u'�'&�-����k'(�u���3^'1�u��s���N'2'u�����)N'8�u��{��-f'D�R��{��-f'DCR��{��-f'D�R��{��-'D�R��{��-7'D�R��{��-'D�R��q�u�{'F����q��f'H����q��f'HC���q��f'H����q��'H�����of'��������f'�C�����\f'������F'������d7'Q����q��uf'R�s��q��uf'RCs��q��uf'R�s��q��u'R�s��q��u7'R�s�����Xf'X�{�����Xf'XC{�����Xf'X�{�����X'X�{9�;��'@�
��YW	Y�<�<�1���<�203!!#!5!��o�����o�\���]��u=� @����	Z[Z���1���0"32654&'2#"&546PnnPPnoO@v+..�����ooPOmmOOp�1.-rB���������#�!Q@+��������"	"���<�<<�221��<�������9990%&&'667#&73����J�DF�HA�Mf�	�fI��X⸹���)*��*'�#�� 32��!�b�`@!�
�����
	�<��<���1/�2����<�2�990K�TX���@878Y�66].#"!!!!53#535632NL�=�t��y-������=���))��׏�/��я��\�=��>�@</0*06
'�&
�*�&�#��<�#?/0-	6W9-W 	W"9&"W3?����������99991��������99990K�
TK�T[K�T[K�T[X�?@??��878Y>54&.#"#"&'532654/.5467.54632{?>��?>��S�8al�Ӄ\]>9̭I�XW�:fq�ր][;;ȦI��.Z.L��-[.K���''PGZsw�eZ�54m@���''TLf{x�f[�1,pE��3��!�	�\��1��04632#"&3�~|��}}��|��|}����;9�
%@��]]����91�2��90!###&&54$y�������f��Nݸ������/�@0-'!
 *��*���.	
 !'	$'$-F0��������99991/�����990@@'(��







 
!"&
 : :!MM I!I"jj ��� ]]4632#"&'532654&/.5467.#"#�������:A9�`��@�IP�Atx;e\`W���q���q����s`/Q*%j�d���_[?T>7;�[�gp������/8L`@6EBC?2�H0�9JC�9����$HE301B<?96I1`K6`C<^	K^	_*M��������299991/���2���9990"32676654&'&&'2#"$'&5476$#32654&'2#'&&###��^^``^^⃄�^]]^\^ㄘmmllmm������mmllmm}{{nWXf��i`C.���;I6B�f^^^傁�^^__^]⃅�]^^gnmm������mmnnmm��mmn�b��>KL?gwyVpMI��`3��D��/IC@&=�>:�A�$1�04�G���$�7aD=0^*	D^	J�����2�1/���������02#"$'&5476$"32676654&'&&&&#"3267#"&54632�mmllmm������mmllmm���^^``^^⃄�^]]^\^�B�B����@zBC�F���I��nmm������mmnnmm��mmng^^^傁�^^__^]⃅�]^^��! ����"����'�F�>@!
	�
�	b	b
cbc�������91�<<�2�<<�903#######5J���q�7�rq�r������/�B^��^s�Rf1@	��D��1��0K�	TK�T[X���@878Y3#����f���F)�@��dd���1�<�20K�
TK�
T[X�@��878YK�TK�
T[K�T[K�T[X���@878YK�TK�T[X�@��878Y@````pppp]3#%3#^��y������'��>@"

���
�
	�<�291�<�2�<��<�990!!!!!'7!5!7!�}�/�H�{����}�������;fը��fӪ�H��@9
B�������
	


��<�2���91/<��������0KSX����Y"��]@gww������	]!!!!!!#!5��9��=���q����ժ�F�����՞��f���	+�@<
+,

)& 
*&�&
��&�,+,* #
)#3,���999999991������99999990@*WZWU!je!{vu!	FYVjddj(|svz(]]	324&'.#"&5!27!"&''��3>�_�'y=�_��''�NOy;��W�f�NP���ƀ�[�gX��@CHp��@C����p�D�f�b�MK�Y�g������[KK�X����/�@-	!$'!!0$*0����99991�����9990@�	


	
$$$   $$	$
***///***55500055	5
:::???:::EEE@@@EE	E
JJJOOOJJJV�� �!�"�&�'�(�)]]32654&#".#"326#"&54632>32#"&�1�Te�vYR��1�UfvYR��F�^����_�HD�a����^�/XZ�ie��7XX�je���ߦ��~���᧯�w��.@���	�

�<�2�<�21/��<�<�0!!#!5!!!�-�Ө��-�����}��}���������
T@.����B���	$#�<�2291/���90KSX����Y"	5!!�@�����������p�o�����
V@/����B���$	#�<<�291/���90KSX����Y"55!5����A�����������Ǫ�R���@F


B	��	
�	fe
f
e�<�2��2�99991/�2�<�2�<�290KSX����Y"K�TX���@878Y@(�����''
')((79���
���]]!#!5!5'!5!3	3!!!��c�`�T���þ{y�����T��9�{3�{J�D���{�3��V�` M@%	��
��!		NF!��2���91��2�<���990�"`"�"]3326533267#"&'#"&'�������#%	 )I#ER2�bf�*�V
�H�������<9�NPOONN��h���-)b@'!	'!�*$$*����9991������990K�TK�T[K�T[K�T[K�T[X�*@**��878Y>54&#"#"&54632#"&54324&#"32�IH7�$$0�e���՘�ݢe�WOm�VPm�mW�K��t,>b������Fأ�[�t}���t{�w;�]@

��91����990@0QVPZ
spvupz
��Z	pp{	t����]]!!	!!5	7�A��J���I���3���!���wq�@���gg����1���20!#!#���
����}������/#�@1��"��$#"#h#$����9999991/<���22�9990K�
TX�$��$$@878Y@V		

	


	
	##(]]#3267#"&5467!##"#>3!��i/7.%7vy"P��µ�)6<	���y��J\:1�fd.��x�o�@E�}/���%&@ �
���&
iji&���1������026732#"&'&&#"#"&546327j��Pd@7*8	k��OeD=!0

���l9�TA6?&#H���n�!�bSA8?Ss�;�)_@3(%���%�
���
��*
"(kl"k
*����22��9991����������99990!!#5#"&5463354&#"56632"32655���P��,�]�����uu>�DI�E����~bRh�P{��@p?D�q��[[""��CO@M�r`�d�.@���
���
klk
���991�����0!!2#"&546"32654&���PX��γ��гi~hi}|P{ݿ��ܾ��s��������N��@@"	��� mm ������9991/<�2��0%!5654#"!5!&5! ��������Dz��?��1/������aL��"�����a���*�>��w���؍{��o{3>@C'-%=
4�%���:�.�-�*��1
��%?47&%7&
=&-7"E?����<���999991��2�<��<����2����9990@�0+0,0-0.0/00@+@,@-@.@/@0P+P,P-P.P/P0�+�0�@�@�@�@�@�@�@�@�@???
??0,0-0.0/@,@-@.@/P,P-P.P/ooo
oo`,`-`.`/p,p-p.p/�,�-�.�/]q].#">32!3267#"&'#"&5463!54&#"5>32"326=�����DJԄ���̷h�dd�j��MI؏�����`�Te�Z���߬�o�������0Z^��Z��55�*,ywxx�����..�''`�f{bsٴ)H����	+�@<+,&

)& 
*&�&
��&�,+,* #
#Q)E,��2��2�9999991������99999990@p(?-YVUV jf!{	
{
z{ {!"#$%{&��%��-�-&YVUZ(ifej(ztvz(���$��$]]	32654&'.#".5327#"&''��)gA��\*g>��}66�]�C�_�56���`�?�`!��*(��Ou�))��Hn.�M�w834�O�M�x���43�N����� $�@/ ��!�#��#�%"	"	"!&	%������99991�������9990K�TK�T[K�T[X�%��%%@878Y@ttttv]33267#"&546?>7>5#53�7ZZ:3�mN�`^�g��IYX0&���D�e�WX5^1YnFC�98ŸL�VV/5<6�5�	b@��
�<�2991/��0K�TX�
@

��878YK�TK�T[K�T[X�
��

@878Y� P]#53#3������+�e����^@
����1���0!#!����^���=��}
*@
	
	��91���903##'%\�s��B��}}`��s-Pb�;�V#�@@

	B

�������!�$	
$��91�2�������299990KSX�2�9Y"K�TX�$��$$@878Y.#"!!#"&'53267#5!>32&P,`r<��:��:d/4a/am"��?$Ɨ5d�z��ɏ������!!����J�����;?@.9*-"*�19�"��<-<�<�21�������9999990#"'&'&'&#"56632326#"'&'&'&#"56632326�i�an�
�^X�bi�an��^V�gi�an�
�^X�bi�an�
�^V�o�NE;=LT�NE;=KڲOE;=LS�NE;=K��`�8@��91/��90@cmpxyvn]]	!3!��^D���C�����?��%#
�@I����
�	�		�
	

�
B	��	o
on��<��2991�<�2990KSX��������Y"55%��-�+#��-�+#�����R������R��H#
�@I�
	
	�

	
������B
��o
op�<��<�991�<�2990KSX��������Y"5%5��+-����+-��#�^R�^���^R�^���#@�
	�����1/<<�220%3#%3#%3#����������������hk'$�u��h^'$�u��s���^'2'us�;@�����	

�������299991/��2��2�0!!!!! !# !3���9���O�A��g�����@A�ժ�F���|pm|������q���{'3�@1���.�(��"%��4"1	K1	Q+E4������9991�2�<���2��2���90@%?5_5p5�5�5�5�5?????	ooooo	]q].#"!3267#"&'#"32>32%"32654&
����H��̷j�bd�j��QGь����BN����������������5Z��44�*,nmnm98olkp�݇���������y���/�1��0!!�y��y��/�1��0!!�y���m�'@�	�	
�������1�<�20#53#53�Ӥ�R�Ӥ�R�?����?����m�'@	��	
��������1�<�203#%3#Ӥ�R�Ӥ�Rլ��@����@����@�����1��0#53�Ӥ�R�?������@��q����1��03#Ӥ�R՘��?���o)@���
	r��<�<�1�����03#3#!!��������o���A��#�u"@��91��990	�����������9%���-��=�V'\�^�����N'<su������+@B����1��0KSX��Y"3#-��\���^R��#/�@I	-'!-
-���'��!0*$0*	$
$(st*(s0������9999999991�������9999999907'#"&''7&&5467'766324&#"326{�r�%$&(�r�;t=:x=�q�%%&&�s�7t@<u\�rp��qq���s�;w>?s9�q�(&%%�s�>v:@t8�s�'%$�|p��pr����s#G@%����B��on��291��90KSX����Y"5s��-�+#�����R���#I@&����B��op�<�91��90KSX����Y"5��+-��#�^R�^�/J�@(�����	�
L�<�2�<��2991/<�2����2990K�
TX���@878YK�TX�@��878Y@0P������]]#!##53546;#"3#J���������cM���`���/яN���Phc��/J�@!�	��	�


L�<��<��991/<�2���2990K�
TX���@878YK�TX�@��878Y@0P�
�����]!#!"!!##53546J���cM/�ѹ�����{Phc��/яN��9�;��>@ ��

��Y
W	Y�<<�2�<<�21���2�2�2�20%!#!5!!5!3!!!�����o��o�o��o�\�����\����H�F����1��03#���F�����@������1��0%3#Ӥ�R����@��m�'@	��	
��������1�<�20%3#%3#�Ӥ�R�fӤ�R����@����@q��
L�#'3?K�@D$%&%&'$'B@�.��(�F�4�:&�$�L%IC'1+C
=	

1
=I
7+
!L�����������991�2�<<�2�2���20KSX��Y"K�TK�	T[K�T[K�T[K�
T[K�T[X�L@LL��878Y"32654&'2#"&5462#"&546!3#"32654&2#"&546"32654&�WddWUccU��������t�������%��Z�VcbWWcd���������WccWUcc���������ܻ��ۻ���ۻ��ۼ��������������ܻ��ۻ������������hm'$�u����m'(�u��hk'$�u����N'(�u����k'(�u���k',�/u����`m',�/u��XN',�/u��;�k',�/u��s���k'2'u��s���m'2'u��s���k'2'u�����)k'8�u�����)m'8�u�����)k'8�u�y` ��F��1/�0@@P`p]3#���`����?f7@��u��91��290K�	TK�T[X���@878Y3#'#�������f�����J7c@$���VwVv���99991�<��<�99990K�	TK�T[X���@878Y'.#"#>3232673#"&�9!
&$}f[&@%9!
&$}f[&@Z7IR��!7IR���b+�/�����1��0K�	TK�T[X���@878Y!!�V�����)9H
W@��VV����1�<��0K�	TX���@878YK�TK�T[K�T[X�@��878Y332673#"&�vaWV`
v
����HKKJL����Df,@	��d��1��0K�	TX���@878Y3#������_@��	��VxV����1����0K�	TK�T[X���@878YK�
TK�T[K�T[X���@878Y4&#"3267#"&54632�X@AWWA@Xz�ss��ss��?XW@AWX@s��ss��#�u�@	

�'	�����1/��90!#"&'532654&'T76xv.W+"J/;<+->i0Y[�0.W=���fB@������991�<�20K�	TK�T[X���@878Y3#3#������߉f��x��L�u� @
��
'�����1/���90!33267#"&546�w-+76 >&Dzs5=X..�

W]0i��?f7@��u��91�<�90K�	TK�T[X���@878Y373��������x������u�
?@
��:	y�<��<�991/��90�0P]3%!!'7��9P�w�^�M����o���;jn�H^@
	�	
z
z�<�<�991/�90K�TX�@��878Y@
@
P
`
sz
p
�
�
	]37#'7Ǹ}Lɸ{J���Zj����Xj�������m'6�u��o���f'V���\m'=�u��X�f']�����@
���<�21����0##������
��
�
��g@ 	�
���
2

y�<�2����91/�2���20@( ��	�
�������	�
������]]! )#53!!3 !�����i�P�`���P���5��������~��������.,q��u('@^%{&%#${##{#({'(#&'('%$%(('"#" !  B('&%"! ##�	��#�)&'! (%#"QE)����99999991�����9990KSX��������Y"�?*]@v%+("/#/$)%-&-'*(6%F%X X!` `!f"u u!u"%#%$&&&''(6$6%F$E%Z Z!b b!z{	

{zzv v!x"�*�*']].#"32654&#"5432''%'3%F2X)������6	~r������4*���!��M!����ü�޼z�&�����7��7�kc\̑oab�����k'<su��=�Vf'\�^���=@�	
���	?
��22��91/����0@	?_]332+#32654&#�������������������'�ђ�����V�>@������GF��22��1��������0@	`���]%#3>32#"&4&#"326s��:�{���{�8��������������da��������a���������-������1��0!!���ת?���@M
�
	������
�	
B
	
	�<�291�<�290KSX��������Y"	'7��7�w�5�5v�8v��L�5�7y�5y��y�5�����
,@�	��	��|]||����1������2035733!������
c)t'�+n^���J@$}}B�����~�����91�����90KSX�2�Y"!!56754&#"56632���"?XhU4zHM�9����8rn81^BQ##{�l���0b���(H@'
�	� �
�	��� �#�)~&~	)�������9991���������90#"&'532654&##532654&#"56632\e��9}F4wCmxolV^^ad_(fQI�7��Z`mR|�yOFJLl?<:=svcE`������'�'�5	��d�����?�'�'�5���d��b���'�'�5	��d��s���m'*
u��q�VZH'J������P',�/u����u��'6����o�u�{'V���s��'k'&-u��q���f'F����s��'m'&-u��q���f'F��q���$J@$�	�"��
���
GE%���<�<�1/�������<�20�`&�&�&]!5!533##5#"3232654&#"���F����:�|���|��ǧ��������N}��}���daDDa��������d������1��0!!d�����H�F����1��03#���F�����1@: �"+�	������/��)	2+"!)#&

	, &
&*!/<��2���99999999991�2��������2�<�20K�	TK�T[K�
T[K�T[K�T[K�T[X�2��22@878Y@z1Tilnooooiko o!o"o#n$l%i'i-���	�
���
�������� �!�"�#�$�%�&�'�(�)�*�+�,�-2		  	USjg
]].#"!!!!3267#"#734&5465#7332�[�f�� A7���8�� ʝf�[Y�`��(�7��7�(6�b�b�iZȻ{.# .{��Zi�HH"{/ #/{"G�)��@
�dd���1�<�20K�TK�T[X�@��878YK�TK�
T[K�T[X���@878YK�TK�T[X�@��878YK�TX���@878Y@````pppp]3#%3#^��y������s���@B���1��0KSX��Y"K�TX���@878YK�TX�@��878Y@ %%6FVjg	//]]3#7������J�u@!���VV	����99991�<���2990K�TX���@878YK�TX�@��878Y�]'.#"#4632326=3#"&�9$(}gV$=09" (}gT";9!2-ev
3)dw����@B���1��0KSX��Y"K�TX���@878YK�TX�@��878Y@*$$5CUU����//]]#������1�w@
���91�<�90K�TX���@878YK�TX�@��878YK�TX���@878Y@//-	]3#'#��Ӌ����������1��@
���91��290K�TK�	T[K�
T[K�T[X���@878YK�TX�@��878YK�TX���@878Y@ " 	]373�Ӌ�����
����?���
�@�	��
]��<��291��<�290K�TK�T[K�T[K�T[K�T[K�
T[X�@��878YK�TK�T[X���@878Y@T/9IFYi���

"5GK
S[
e���]]	!33##5!5��5�����bf�]��m��y�9�
j@��VV����1��20K�TX���@878YK�TX�@��878YK�TK�T[X���@878Y332673#"&�v
cSRav
�����6978w{z�f�����1��03#�����	

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~�������������������������������������������������������������������������������������������������>: ~�1BSax~����� & 0 : �!""""+"H"e%��� �0AR^x}�����  0 9 �!""""+"H"`%������������^�C�h��������V�j�q�_���8����� (B�����������������������������������bc�d�e�������f����g�����h���jikmln�oqprsutvw�xzy{}|��~����������������������������������������������f��55������q=�3���=�����d�������d���������������?��y}��s)����3s�\�\��?�u�����Ls��Ls������y�D{=�{\���������{�fqq�q�/q�9�9����9�����q�qJ�+o#7��=�V�;�=3X���yy�s���Ls���{�{�{�{�{�{fq�q�q�q�q9�9��9��9����q�q�q�q�q����9���\�3�
�'s����Lf��������R�#hd����+/�s�`N�{�H?�55��=��Z�������yyLs�s/q%�%���������=�V��^3�3�
/
/9����%�
�qy�y��\�\��\\;LsLsLs������9�������#�L���F�+o{\3X�3
�q��=������5�5^5b�����b3sq\��+o�sfq�sfqq�d���s���5?���+�	

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~������������������������������������������������������������������������������������������������������������������������������	

	sfthyphenperiodcenteredEuroc6459c6460c6461c6462c6463c6466c6467c6468c6469""""X���O���!n����E�~�L��e��		R	s	�

�X��:i��

=
z
/�E��u�)p����P���@�"m�#{�C��w�R�������w� [ r �!5!B!�!�!�"	""#"0"="J"W"d"q"~"�"�"�"�"�"�"�"�"�##
##'#4#A#N#[#h#�#�$4$�%3%S%�&&�'K'�'�((X(�)_*%*\*�*�+z+�,D,�,�-P-�..R.�/�0A0�11!1P1�2H2z2�3F3p3p3}3�3�3�4z4�4�4�4�5595g5�5�5�5�6[6�6�7C7�7�888J999)969C9P9]9j9w9�9�9�9�9�9�9�9�::{:�:�;;^;�;�;�<"<_<�<�<�<�<�<�=c>;>H>U>�>�>�?a?�?�@:@K@\@m@z@�@�@�@�@�@�@�@�A@AVAkBEB�B�C_C�C�DUD�E*E?-��
x$��%��&')*K+-r./2934K57�D9��:��;��<�
=IQR&UY��Z\��b��dg9xy&z&{&|&}&���������9�����
�����9�9�9���
����K��$��$��$��$$9$&��$*��$2��$4��$6$7�a$8$9�}$:��$;$<�a$F��$G��$H��$I��$R��$T��$W��$X$Y��$Z��$\�u$b9$d��$g��$h$o��$p��$q��$r��$s��$y��$z��${��$|��$}��$~$$�$�$���$�$�9$�9$���$���$��$��u$��a$�/$�9$�9$��$��$��$�$�$�$�$�a$�u$���$�$���$���$���$���%%&��%*��%2��%6��%9��%:��%<��%d��%g��%���%���%���%���%���%���%��%��%��%��%��%�%���%���%���%���&&$&6&<��&b&���&���&�&�&�&�&&���&�&�&�&�&��&�''$��'9��':'<��'b��'���'���'���'���'���'���'���'�D'��'��'�))��)�a)$�D)6��)7��)D�D)H��)L�k)R��)U�k)X��)\�D)b�D)i�D)j�D)k�D)l�D)m�D)n�D)p��)q��)r��)s��)y��)z��){��)|��)}��)~��)��)���)���)�)�)��D)��D)���)�)��D)��)�D)�D)��)�D)���**$*7��*:*<��*b*���*���*�*�*���*���*���*��*�*�*�++��++�+�+���+���+��-��-$��-b��-���-���-���-���-���-���-��-��-��.�).$��.&��.2��.7�a.8��.:��.<��.D��.H��.R��.X��.\�k.b��.d��.g��.h��.i��.j��.k��.l��.m��.n��.p��.q��.r��.s��.y��.z��.{��.|��.}��.~��.��.���.���.��}.�.���.���.���.���.���.��k.���.�.��.��.��.��.��.��.��.��.�.�k.���.���/��/$//2��/7��/8��/9�/:�D/<��/D/H��/R��/X��/\�D/b//g��/h��/i/j/k/l/m/n/p��/q��/r��/s��/y��/z��/{��/|��/}��/~��/��/���/���/�/�/�//�//���/��a/���/��D/���/�/�//�//��/��/��/��/��/��/��/�D292��2��2$��29��2;�}2<��2b��2���2�2���2���2���2���2���2�D2��2��2�3��3��33$�}383:3<��3D��3H��3L��3Q��3R��3U��3V��3X��3\3b�}3h3i��3j��3k��3l��3m��3n��3p��3q��3r��3s��3x��3y��3z��3{��3|��3}��3~��3��3���3���3���3�3��}3��}3�&3�&3�3���3��3�}3�}3�3�3�3��3��3�3���494�4�4���4���4�}5��5��5��5$��5&��57�k59��5:��5<�}5D��5H��5R��5X��5\��5b��5d��5i��5j��5k��5l��5m��5n��5p��5q��5r��5s��5y��5z��5{��5|��5}��5~��5��5���5���5���5���5���5���5��k5��}5���5��}5��5��5��5�}5�5���5���6$&6&6*6264666b&6d6g6�&6�&6�6�&6�&6�6�6�6�6�6�6�6�7�D7�
7�7$�a7&��77��7D��7F��7H��7L��7R��7U��7V��7X��7Z��7\��7b�a7d��7i��7j��7k��7l��7m��7n��7o��7p��7q��7r��7s��7y��7z��7{��7|��7}��7~��7��7���7���7��D7���7��a7��a7�7���7���7��7�a7�a7�7�7���7���7���7���7���8$8-8=��8b8�8�8�8�8��9��9��9�Y9$�}92��9D�a9H�a9L��9R�a9X�u9\��9b�}9g��9i�a9j�a9k�a9l�a9m�a9n�a9p�a9q�a9r�a9s�a9y�a9z�a9{�a9|�a9}�a9~�u9�u9��u9��u9��N9���9��}9��}9���9�9�9���9��9�}9�}9��9��9��9��:��:�:��:$��:D�}:H��:L��:R��:U��:X��:\��:b��:i�}:j�}:k�}:l�}:m�}:n�}:p��:q��:r��:s��:y��:z��:{��:|��:}��:~��:��:���:���:���:���:���:���:���:�:���:��:��:��:��;��;$;&�k;2�};7��;H��;b;d�k;g�};p��;q��;r��;s��;���;�;�;�;��};��a;���;��;�;�;�};�};�};��k;��k<�
<�a<��<$�a<&��<2��<D��<H��<L��<R��<X�<b�a<d��<g��<i��<j��<k��<l��<m��<n��<p��<q��<r��<s��<y��<z��<{��<|��<}��<~�<�<��<��<��<��k<��a<��a<���<���<���<��<�a<�a<��<��<��<���<���=��=�=�=���=���=��H[��I��I�kI��IW��IZ��I\��I���I���I�AI�I���I�I��ND��NH��NR��NX��N\��Ni��Nj��Nk��Nl��Nm��Nn��Np��Nq��Nr��Ns��Ny��Nz��N{��N|��N}��N~��N��N���N���N���N�QQQQ�Q�Q��kQ���Q��R&R��RR[��R�R�R��kR���R�}U�}U�DU��UF��UG��UH��UIUJ��UK��UP��UQ��UR��UT��UU��UXUYUZU[��U\U]Uo��Up��Uq��Ur��Us��Ux��Uy��Uz��U{��U|��U}��U~UU�U�U���U�U�U�VU�U��U�U�U���U���U���Y��Y�aY��Y���Y���Y�Y���Y��ZZ�DZ��Z���Z���Z�Z�Z�)[F��[H��[R��[o��[p��[q��[r��[s��[y��[z��[{��[|��[}��[���[���\��\��\�k\���\���\�\�\��b��b��b��b$9b&��b*��b2��b4��b6b7�ab8b9�}b:��b;b<�abF��bG��bH��bI��bR��bT��bW��bXbY��bZ��b\�ubb9bd��bg��bhbo��bp��bq��br��bs��by��bz��b{��b|��b}��b~bb�b�b���b�b�9b�9b���b���b��b��ub��ab�/b�9b�9b��b��b��b�b�b�b�b�ab�ub���b�b���b���b���b���dd$d6d<��dbd���d���d�d�d�d�&d���d�d�d�d�d��d�g9g��g��g$��g9��g;�}g<��gb��g���g�g���g���g���g���g���g�Dg��g��g�h$h-h=��hbh�h�h�h�h��p[��q[��r[��s[��xxxx�x�x��kx���x��y&y��yy[��y�y�y��ky���y�}z&z��zz[��z�z�z��kz���z�}{&{��{{[��{�{�{��k{���{�}|&|��||[��|�|�|��k|���|�}}&}��}}[��}�}�}��k}���}�}�&��������������������������������$�%���&���'���)�*���+�-���.�/�2�3�4�5�7���9���:���;�<�k�=�I�Q�R�U�Y���Z���\���b�d���g�x�y�z�{�|�}������������������k����������������k������������������$���%���&���'���)�*�+�-���.�/�2���3�4�5�7�D�9�N�:���;���<��=�I�Q�R�U�Y���Z���\���b���d���g���x�y�z�{�|�}���������������������������������������������������������������������$9�&���*���2���4���6�7�a�8�9�}�:���;�<�a�F���G���H���I���R���T���W���X�Y���Z���\�u�b9�d���g���h�o���p���q���r���s���y���z���{���|���}���~�������������9��9��������������u���a��/��9��9�������������������a��u��������������������������������$9�&���*���2���4���6�7�a�8�9�}�:���;�<�a�F���G���H���I���R���T���W���X�Y���Z���\�u�b9�d���g���h�o���p���q���r���s���y���z���{���|���}���~�������������9��9��������������u���a��/��9��9�������������������a��u�����������������������9�������$���9���;�}�<���b������������������������������D���������$���%���&���'���)���*���+���-���.���/���2���3���4���5���7�9�:�;���<�=���I���Q���R�k�U���Y���Z���\���b���d���g���x���y�k�z�k�{�k�|�k�}�k�������}����������������������������������������������������������������k�����������������
��a����$�a�&���2���D���H���L���R���X��b�a�d���g���i���j���k���l���m���n���p���q���r���s���y���z���{���|���}���~���������������k���a���a�����������������a��a������������������$&�%���&���'���)���*���+���-/�.���/���2���3���4���5���7���9���:��;���<���=�I���Q���R���U���Y��Z�<�\���b&�d���g���x���y���z���{���|���}��������&��&��&��������������&��&�����������������������������������������$9�&���*���2���4���6�7�a�8�9�}�:���;�<�a�F���G���H���I���R���T���W���X�Y���Z���\�u�b9�d���g���h�o���p���q���r���s���y���z���{���|���}���~�������������9��9��������������u���a��/��9��9�������������������a��u��������������������������������$9�&���*���2���4���6�7�a�8�9�}�:���;�<�a�F���G���H���I���R���T���W���X�Y���Z���\�u�b9�d���g���h�o���p���q���r���s���y���z���{���|���}���~�������������9��9��������������u���a��/��9��9�������������������a��u�����������������������9�������$���9���;�}�<���b������������������������������D���������9�������$���9���;�}�<���b������������������������������D���������9�������$���9���;�}�<���b������������������������������D���������$�-�=���b������������$�-�=���b������������$�-�=���b������������$&�&�*�2�4�6�b&�d�g��&��&����&��&����������������������������������������������������
��a����$�a�&���2���D���H���L���R���X��b�a�d���g���i���j���k���l���m���n���p���q���r���s���y���z���{���|���}���~���������������k���a���a�����������������a��a�������������������������k������������������k���������������D��$�7���:�<���b����������������������������������$&�&�*�2�4�6�b&�d�g��&��&����&��&������������������$�6�<���b����������������&�������������������$�6�<���b����������������&�����������������MB@hm�
�����
L�G��BG�S�f�� JBits@ ���m�B��`#c�VeraSans����6���628R00@		
							
					


			












	

																															






			
						
	
			
	



		

	


								

	

			





	
	


				






	


	
		


		





	




				
											

	
	
	
		
	
	
					
									
	

																																					


	
						

			

















		

	

	

	
											





																					

	
	




	
		


		



	
		
	
		
				

	


	
















	
	
	



		
	
	
	

	



	
	
		




											











	








	
	







		








	
		
		





			



		



	





		




























			
		
	
	

	


			



		





										







								
	






		
	
	






								
																

	
	
									









		


	







		






	













	
	
	
	





















































		
































































	




















































	
		



		



		



















	


		




							

		

		
			
			

	 
			

		

	





 






	
"



	




	

















"















	







#



				
	


	
	














#









	


	







$			
					
	

		
		$	
	&		
	
					
	

				&	
	��P
�_<������g����
LmPK��\��P  gmagick-063-levelimage.phptnu�[���--TEST--
levelImage test
--SKIPIF--
<?php
/* $Id: gmagick-063-levelimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->levelImage(0.2, 0.2, 0.2);
echo "ok";
?>
--EXPECTF--
ok
PK��\�d,�gmagick-069-trimimage.phptnu�[���--TEST--
trimImage test
--SKIPIF--
<?php
/* $Id: gmagick-069-trimimage.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$gm = new Gmagick();
$gm->read("magick:rose");
$gm->trimImage(0.5);
echo "ok";
?>
--EXPECTF--
okPK��\0�z�44#gmagickpixel-003-getcolorcount.phptnu�[���--TEST--
Test getColorcount method
--SKIPIF--
<?php
/* $Id: gmagickpixel-003-getcolorcount.phpt 280206 2009-05-09 18:22:48Z vito $ */
if(!extension_loaded('gmagick')) die('skip');
?>
--FILE--
<?php
$color = "rgb(255,255,255)";
$pixel = new GmagickPixel($color);
echo $pixel->getColorCount();
?>
--EXPECTF--
0PK��\��.��� gmagick-104-setgetimagepage.phptnu�[���--TEST--
Gmagick::setImagePage test
--SKIPIF--
<?php
require_once(dirname(__FILE__) . '/skipif.inc');

checkClassMethods(
    'Gmagick',
    array('setImagePage', 'getImagePage')
);

?>
--FILE--
<?php
$image = new Gmagick('magick:rose');
$image->setImagePage(10,10,10,10);
$result = $image->getImagePage();
if (($result['width'] === 10) && ($result['height'] === 10) 
    && ($result['x'] === 10) && ($result['y'] === 10)) {
    echo "ok";
}
?>
--EXPECTF--
okPK�[�\|I�&6t6tRedisClusterTest.phpnu�[���<?php defined('PHPREDIS_TESTRUN') or die("Use TestRedis.php to run tests!\n");
require_once(dirname($_SERVER['PHP_SELF'])."/RedisTest.php");

/**
 * Most RedisCluster tests should work the same as the standard Redis object
 * so we only override specific functions where the prototype is different or
 * where we're validating specific cluster mechanisms
 */
class Redis_Cluster_Test extends Redis_Test {
    private static $_arr_node_map = [];

    private $_arr_redis_types = [
        Redis::REDIS_STRING,
        Redis::REDIS_SET,
        Redis::REDIS_LIST,
        Redis::REDIS_ZSET,
        Redis::REDIS_HASH
    ];

    private $_arr_failover_types = [
        RedisCluster::FAILOVER_NONE,
        RedisCluster::FAILOVER_ERROR,
        RedisCluster::FAILOVER_DISTRIBUTE
    ];

    /**
     * @var string
     */
    protected $sessionPrefix = 'PHPREDIS_CLUSTER_SESSION:';

    /**
     * @var string
     */
    protected $sessionSaveHandler = 'rediscluster';

    /* Tests we'll skip all together in the context of RedisCluster.  The
     * RedisCluster class doesn't implement specialized (non-redis) commands
     * such as sortAsc, or sortDesc and other commands such as SELECT are
     * simply invalid in Redis Cluster */
    public function testSortAsc()  { return $this->markTestSkipped(); }
    public function testSortDesc() { return $this->markTestSkipped(); }
    public function testWait()     { return $this->markTestSkipped(); }
    public function testSelect()   { return $this->markTestSkipped(); }
    public function testReconnectSelect() { return $this->markTestSkipped(); }
    public function testMultipleConnect() { return $this->markTestSkipped(); }
    public function testDoublePipeNoOp() { return $this->markTestSkipped(); }
    public function testSwapDB() { return $this->markTestSkipped(); }
    public function testConnectException() { return $this->markTestSkipped(); }
    public function testTlsConnect() { return $this->markTestSkipped(); }
    public function testInvalidAuthArgs() { return $this->markTestSkipped(); }

    public function testlMove() { return $this->markTestSkipped(); }
    public function testsMisMember() { return $this->markTestSkipped(); }
    public function testzDiff() { return $this->markTestSkipped(); }
    public function testzDiffStore() { return $this->markTestSkipped(); }
    public function testzMscore() { return $this->marktestSkipped(); }
    public function testCopy() { return $this->marktestSkipped(); }

    /* Session locking feature is currently not supported in in context of Redis Cluster.
       The biggest issue for this is the distribution nature of Redis cluster */
    public function testSession_lockKeyCorrect() { return $this->markTestSkipped(); }
    public function testSession_lockingDisabledByDefault() { return $this->markTestSkipped(); }
    public function testSession_lockReleasedOnClose() { return $this->markTestSkipped(); }
    public function testSession_ttlMaxExecutionTime() { return $this->markTestSkipped(); }
    public function testSession_ttlLockExpire() { return $this->markTestSkipped(); }
    public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { return $this->markTestSkipped(); }
    public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { return $this->markTestSkipped(); }
    public function testSession_correctLockRetryCount() { return $this->markTestSkipped(); }
    public function testSession_defaultLockRetryCount() { return $this->markTestSkipped(); }
    public function testSession_noUnlockOfOtherProcess() { return $this->markTestSkipped(); }
    public function testSession_lockWaitTime() { return $this->markTestSkipped(); }

    /* Load our seeds on construction */
    public function __construct($str_host, $i_port, $str_auth) {
        parent::__construct($str_host, $i_port, $str_auth);

        $str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap';

        if (!file_exists($str_nodemap_file)) {
            fprintf(STDERR, "Error:  Can't find nodemap file for seeds!\n");
            exit(1);
        }

        /* Store our node map */
        if (!self::$_arr_node_map) {
            self::$_arr_node_map = array_filter(
                explode("\n", file_get_contents($str_nodemap_file)
            ));
        }
    }

    /* Override setUp to get info from a specific node */
    public function setUp() {
        $this->redis = $this->newInstance();
        $info = $this->redis->info(uniqid());
        $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0');
    }

    /* Override newInstance as we want a RedisCluster object */
    protected function newInstance() {
        return new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth());
    }

    /* Overrides for RedisTest where the function signature is different.  This
     * is only true for a few commands, which by definition have to be directed
     * at a specific node */

    public function testPing() {
        for ($i = 0; $i < 20; $i++) {
            $this->assertTrue($this->redis->ping("key:$i"));
            $this->assertEquals('BEEP', $this->redis->ping("key:$i", 'BEEP'));
        }

        /* Make sure both variations work in MULTI mode */
        $this->redis->multi();
        $this->redis->ping('{ping-test}');
        $this->redis->ping('{ping-test}','BEEP');
        $this->assertEquals([true, 'BEEP'], $this->redis->exec());
    }

    public function testRandomKey() {
        /* Ensure some keys are present to test */
        for ($i = 0; $i < 1000; $i++) {
            if (rand(1, 2) == 1) {
                $this->redis->set("key:$i", "val:$i");
            }
        }

        for ($i = 0; $i < 1000; $i++) {
            $k = $this->redis->randomKey("key:$i");
            $this->assertTrue($this->redis->exists($k));
        }
    }

    public function testEcho() {
        $this->assertEquals($this->redis->echo('k1', 'hello'), 'hello');
        $this->assertEquals($this->redis->echo('k2', 'world'), 'world');
        $this->assertEquals($this->redis->echo('k3', " 0123 "), " 0123 ");
    }

    public function testSortPrefix() {
        $this->redis->setOption(Redis::OPT_PREFIX, 'some-prefix:');
        $this->redis->del('some-item');
        $this->redis->sadd('some-item', 1);
        $this->redis->sadd('some-item', 2);
        $this->redis->sadd('some-item', 3);

        $this->assertEquals(array('1','2','3'), $this->redis->sort('some-item'));

        // Kill our set/prefix
        $this->redis->del('some-item');
        $this->redis->setOption(Redis::OPT_PREFIX, '');
    }

    public function testDBSize() {
        for ($i = 0; $i < 10; $i++) {
            $str_key = "key:$i";
            $this->assertTrue($this->redis->flushdb($str_key));
            $this->redis->set($str_key, "val:$i");
            $this->assertEquals(1, $this->redis->dbsize($str_key));
        }
    }

    public function testInfo() {
        $arr_check_keys = [
            "redis_version", "arch_bits", "uptime_in_seconds", "uptime_in_days",
            "connected_clients", "connected_slaves", "used_memory",
            "total_connections_received", "total_commands_processed",
            "role"
        ];

        for ($i = 0; $i < 3; $i++) {
            $arr_info = $this->redis->info("k:$i");
            foreach ($arr_check_keys as $str_check_key) {
                $this->assertTrue(isset($arr_info[$str_check_key]));
            }
        }
    }

    public function testClient() {
        $str_key = 'key-' . rand(1,100);

        $this->assertTrue($this->redis->client($str_key, 'setname', 'cluster_tests'));

        $arr_clients = $this->redis->client($str_key, 'list');
        $this->assertTrue(is_array($arr_clients));

        /* Find us in the list */
        $str_addr = NULL;
        foreach ($arr_clients as $arr_client) {
            if ($arr_client['name'] == 'cluster_tests') {
                $str_addr = $arr_client['addr'];
                break;
            }
        }

        /* We should be in there */
        $this->assertFalse(empty($str_addr));

        /* Kill our own client! */
        $this->assertTrue($this->redis->client($str_key, 'kill', $str_addr));
    }

    public function testTime() {
        $time_arr = $this->redis->time("k:" . rand(1,100));
        $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 &&
                          strval(intval($time_arr[0])) === strval($time_arr[0]) &&
                          strval(intval($time_arr[1])) === strval($time_arr[1]));
    }

    public function testScan() {
        $i_key_count = 0;
        $i_scan_count = 0;

        /* Have scan retry for us */
        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);

        /* Iterate over our masters, scanning each one */
        foreach ($this->redis->_masters() as $arr_master) {
            /* Grab the number of keys we have */
            $i_key_count += $this->redis->dbsize($arr_master);

            /* Scan the keys here */
            $it = NULL;
            while ($arr_keys = $this->redis->scan($it, $arr_master)) {
                $i_scan_count += count($arr_keys);
            }
        }

        /* Our total key count should match */
        $this->assertEquals($i_scan_count, $i_key_count);
    }

    public function testScanPrefix() {
        $arr_prefixes = ['prefix-a:', 'prefix-b:'];
        $str_id = uniqid();

        $arr_keys = [];
        foreach ($arr_prefixes as $str_prefix) {
            $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
            $this->redis->set($str_id, "LOLWUT");
            $arr_keys[$str_prefix] = $str_id;
        }

        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX);

        foreach ($arr_prefixes as $str_prefix) {
            $arr_prefix_keys = [];
            $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);

            foreach ($this->redis->_masters() as $arr_master) {
                $it = NULL;
                while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) {
                    foreach ($arr_iter as $str_key) {
                        $arr_prefix_keys[$str_prefix] = $str_key;
                    }
                }
            }

            $this->assertTrue(count($arr_prefix_keys) == 1 && isset($arr_prefix_keys[$str_prefix]));
        }

        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);

        $arr_scan_keys = [];

        foreach ($this->redis->_masters() as $arr_master) {
            $it = NULL;
            while ($arr_iter = $this->redis->scan($it, $arr_master, "*$str_id*")) {
                foreach ($arr_iter as $str_key) {
                    $arr_scan_keys[] = $str_key;
                }
            }
        }

        /* We should now have both prefixs' keys */
        foreach ($arr_keys as $str_prefix => $str_id) {
            $this->assertTrue(in_array("${str_prefix}${str_id}", $arr_scan_keys));
        }
    }

    // Run some simple tests against the PUBSUB command.  This is problematic, as we
    // can't be sure what's going on in the instance, but we can do some things.
    public function testPubSub() {
        // PUBSUB CHANNELS ...
        $result = $this->redis->pubsub("somekey", "channels", "*");
        $this->assertTrue(is_array($result));
        $result = $this->redis->pubsub("somekey", "channels");
        $this->assertTrue(is_array($result));

        // PUBSUB NUMSUB

        $c1 = '{pubsub}-' . rand(1,100);
        $c2 = '{pubsub}-' . rand(1,100);

        $result = $this->redis->pubsub("{pubsub}", "numsub", $c1, $c2);

        // Should get an array back, with two elements
        $this->assertTrue(is_array($result));
        $this->assertEquals(count($result), 4);

        $arr_zipped = [];
        for ($i = 0; $i <= count($result) / 2; $i+=2) {
            $arr_zipped[$result[$i]] = $result[$i+1];
        }
        $result = $arr_zipped;

        // Make sure the elements are correct, and have zero counts
        foreach([$c1,$c2] as $channel) {
            $this->assertTrue(isset($result[$channel]));
            $this->assertEquals($result[$channel], 0);
        }

        // PUBSUB NUMPAT
        $result = $this->redis->pubsub("somekey", "numpat");
        $this->assertTrue(is_int($result));

        // Invalid call
        $this->assertFalse($this->redis->pubsub("somekey", "notacommand"));
    }

    /* Unlike Redis proper, MsetNX won't always totally fail if all keys can't
     * be set, but rather will only fail per-node when that is the case */
    public function testMSetNX() {
        /* All of these keys should get set */
        $this->redis->del('x','y','z');
        $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']);
        $this->assertTrue(is_array($ret));
        $this->assertEquals(array_sum($ret),count($ret));

        /* Delete one key */
        $this->redis->del('x');
        $ret = $this->redis->msetnx(['x'=>'a','y'=>'b','z'=>'c']);
        $this->assertTrue(is_array($ret));
        $this->assertEquals(array_sum($ret),1);

        $this->assertFalse($this->redis->msetnx(array())); // set ø → FALSE
    }

    /* Slowlog needs to take a key or [ip, port], to direct it to a node */
    public function testSlowlog() {
        $str_key = uniqid() . '-' . rand(1, 1000);

        $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get')));
        $this->assertTrue(is_array($this->redis->slowlog($str_key, 'get', 10)));
        $this->assertTrue(is_int($this->redis->slowlog($str_key, 'len')));
        $this->assertTrue($this->redis->slowlog($str_key, 'reset'));
        $this->assertFalse($this->redis->slowlog($str_key, 'notvalid'));
    }

    /* INFO COMMANDSTATS requires a key or ip:port for node direction */
    public function testInfoCommandStats() {
        $str_key = uniqid() . '-' . rand(1,1000);
        $arr_info = $this->redis->info($str_key, "COMMANDSTATS");

        $this->assertTrue(is_array($arr_info));
        if (is_array($arr_info)) {
            foreach($arr_info as $k => $str_value) {
                $this->assertTrue(strpos($k, 'cmdstat_') !== false);
            }
        }
    }

    /* RedisCluster will always respond with an array, even if transactions
     * failed, because the commands could be coming from multiple nodes */
    public function testFailedTransactions() {
        $this->redis->set('x', 42);

        // failed transaction
        $this->redis->watch('x');

        $r = $this->newInstance(); // new instance, modifying `x'.
        $r->incr('x');

        // This transaction should fail because the other client changed 'x'
        $ret = $this->redis->multi()->get('x')->exec();
        $this->assertTrue($ret === [false]);
        // watch and unwatch
        $this->redis->watch('x');
        $r->incr('x'); // other instance
        $this->redis->unwatch('x'); // cancel transaction watch

        // This should succeed as the watch has been cancelled
        $ret = $this->redis->multi()->get('x')->exec();
        $this->assertTrue($ret === array('44'));
    }

    public function testDiscard()
    {
        /* start transaction */
        $this->redis->multi();

        /* Set and get in our transaction */
        $this->redis->set('pipecount','over9000')->get('pipecount');

        $this->assertTrue($this->redis->discard());
    }

    /* RedisCluster::script() is a 'raw' command, which requires a key such that
     * we can direct it to a given node */
    public function testScript() {
        $str_key = uniqid() . '-' . rand(1,1000);

        // Flush any scripts we have
        $this->assertTrue($this->redis->script($str_key, 'flush'));

        // Silly scripts to test against
        $s1_src = 'return 1';
        $s1_sha = sha1($s1_src);
        $s2_src = 'return 2';
        $s2_sha = sha1($s2_src);
        $s3_src = 'return 3';
        $s3_sha = sha1($s3_src);

        // None should exist
        $result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha);
        $this->assertTrue(is_array($result) && count($result) == 3);
        $this->assertTrue(is_array($result) && count(array_filter($result)) == 0);

        // Load them up
        $this->assertTrue($this->redis->script($str_key, 'load', $s1_src) == $s1_sha);
        $this->assertTrue($this->redis->script($str_key, 'load', $s2_src) == $s2_sha);
        $this->assertTrue($this->redis->script($str_key, 'load', $s3_src) == $s3_sha);

        // They should all exist
        $result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha);
        $this->assertTrue(is_array($result) && count(array_filter($result)) == 3);
    }

    /* RedisCluster::EVALSHA needs a 'key' to let us know which node we want to
     * direct the command at */
    public function testEvalSHA() {
        $str_key = uniqid() . '-' . rand(1,1000);

        // Flush any loaded scripts
        $this->redis->script($str_key, 'flush');

        // Non existant script (but proper sha1), and a random (not) sha1 string
        $this->assertFalse($this->redis->evalsha(sha1(uniqid()),[$str_key], 1));
        $this->assertFalse($this->redis->evalsha('some-random-data'),[$str_key], 1);

        // Load a script
        $cb  = uniqid(); // To ensure the script is new
        $scr = "local cb='$cb' return 1";
        $sha = sha1($scr);

        // Run it when it doesn't exist, run it with eval, and then run it with sha1
        $this->assertTrue(false === $this->redis->evalsha($scr,[$str_key], 1));
        $this->assertTrue(1 === $this->redis->eval($scr,[$str_key], 1));
        $this->assertTrue(1 === $this->redis->evalsha($sha,[$str_key], 1));
    }

    public function testEvalBulkResponse() {
        $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
        $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';

        $this->redis->script($str_key1, 'flush');
        $this->redis->script($str_key2, 'flush');

        $scr = "return {KEYS[1],KEYS[2]}";

        $result = $this->redis->eval($scr,[$str_key1, $str_key2], 2);

        $this->assertTrue($str_key1 === $result[0]);
        $this->assertTrue($str_key2 === $result[1]);
    }

    public function testEvalBulkResponseMulti() {
        $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
        $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';

        $this->redis->script($str_key1, 'flush');
        $this->redis->script($str_key2, 'flush');

        $scr = "return {KEYS[1],KEYS[2]}";

        $this->redis->multi();
        $this->redis->eval($scr, [$str_key1, $str_key2], 2);

        $result = $this->redis->exec();

        $this->assertTrue($str_key1 === $result[0][0]);
        $this->assertTrue($str_key2 === $result[0][1]);
    }

    public function testEvalBulkEmptyResponse() {
        $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
        $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';

        $this->redis->script($str_key1, 'flush');
        $this->redis->script($str_key2, 'flush');

        $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end";

        $result = $this->redis->eval($scr, [$str_key1, $str_key2], 2);

        $this->assertTrue(null === $result);
    }

    public function testEvalBulkEmptyResponseMulti() {
        $str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
        $str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';

        $this->redis->script($str_key1, 'flush');
        $this->redis->script($str_key2, 'flush');

        $scr = "for _,key in ipairs(KEYS) do redis.call('SET', key, 'value') end";

        $this->redis->multi();
        $this->redis->eval($scr, [$str_key1, $str_key2], 2);
        $result = $this->redis->exec();

        $this->assertTrue(null === $result[0]);
    }

    /* Cluster specific introspection stuff */
    public function testIntrospection() {
        $arr_masters = $this->redis->_masters();
        $this->assertTrue(is_array($arr_masters));

        foreach ($arr_masters as $arr_info) {
            $this->assertTrue(is_array($arr_info));
            $this->assertTrue(is_string($arr_info[0]));
            $this->assertTrue(is_long($arr_info[1]));
        }
    }

    protected function genKeyName($i_key_idx, $i_type) {
        switch ($i_type) {
            case Redis::REDIS_STRING:
                return "string-$i_key_idx";
            case Redis::REDIS_SET:
                return "set-$i_key_idx";
            case Redis::REDIS_LIST:
                return "list-$i_key_idx";
            case Redis::REDIS_ZSET:
                return "zset-$i_key_idx";
            case Redis::REDIS_HASH:
                return "hash-$i_key_idx";
            default:
                return "unknown-$i_key_idx";
        }
    }

    protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) {
        $str_key = $this->genKeyName($i_key_idx, $i_type);

        $this->redis->del($str_key);

        switch ($i_type) {
            case Redis::REDIS_STRING:
                $value = "$str_key-value";
                $this->redis->set($str_key, $value);
                break;
            case Redis::REDIS_SET:
                $value = [
                    $str_key . '-mem1', $str_key . '-mem2', $str_key . '-mem3',
                    $str_key . '-mem4', $str_key . '-mem5', $str_key . '-mem6'
                ];
                $arr_args = $value;
                array_unshift($arr_args, $str_key);
                call_user_func_array([$this->redis, 'sadd'], $arr_args);
                break;
            case Redis::REDIS_HASH:
                $value = [
                    $str_key . '-mem1' => $str_key . '-val1',
                    $str_key . '-mem2' => $str_key . '-val2',
                    $str_key . '-mem3' => $str_key . '-val3'
                ];
                $this->redis->hmset($str_key, $value);
                break;
            case Redis::REDIS_LIST:
                $value = [
                    $str_key . '-ele1', $str_key . '-ele2', $str_key . '-ele3',
                    $str_key . '-ele4', $str_key . '-ele5', $str_key . '-ele6'
                ];
                $arr_args = $value;
                array_unshift($arr_args, $str_key);
                call_user_func_array([$this->redis, 'rpush'], $arr_args);
                break;
            case Redis::REDIS_ZSET:
                $i_score = 1;
                $value = [
                    $str_key . '-mem1' => 1, $str_key . '-mem2' => 2,
                    $str_key . '-mem3' => 3, $str_key . '-mem3' => 3
                ];
                foreach ($value as $str_mem => $i_score) {
                    $this->redis->zadd($str_key, $i_score, $str_mem);
                }
                break;
        }

        /* Update our reference array so we can verify values */
        $arr_ref[$str_key] = $value;
        return $str_key;
    }

    /* Verify that our ZSET values are identical */
    protected function checkZSetEquality($a, $b) {
        /* If the count is off, the array keys are different or the sums are
         * different, we know there is something off */
        $boo_diff = count($a) != count($b) ||
            count(array_diff(array_keys($a), array_keys($b))) != 0 ||
            array_sum($a) != array_sum($b);

        if ($boo_diff) {
            $this->assertEquals($a,$b);
            return;
        }
    }

    protected function checkKeyValue($str_key, $i_type, $value) {
        switch ($i_type) {
            case Redis::REDIS_STRING:
                $this->assertEquals($value, $this->redis->get($str_key));
                break;
            case Redis::REDIS_SET:
                $arr_r_values = $this->redis->sMembers($str_key);
                $arr_l_values = $value;
                sort($arr_r_values);
                sort($arr_l_values);
                $this->assertEquals($arr_r_values, $arr_l_values);
                break;
            case Redis::REDIS_LIST:
                $this->assertEquals($value, $this->redis->lrange($str_key,0,-1));
                break;
            case Redis::REDIS_HASH:
                $this->assertEquals($value, $this->redis->hgetall($str_key));
                break;
            case Redis::REDIS_ZSET:
                $this->checkZSetEquality($value, $this->redis->zrange($str_key,0,-1,true));
                break;
            default:
                throw new Exception("Unknown type " . $i_type);
        }
    }

    /* Test automatic load distributor */
    public function testFailOver() {
        $arr_value_ref = [];
        $arr_type_ref  = [];

        /* Set a bunch of keys of various redis types*/
        for ($i = 0; $i < 200; $i++) {
            foreach ($this->_arr_redis_types as $i_type) {
                $str_key = $this->setKeyVals($i, $i_type, $arr_value_ref);
                $arr_type_ref[$str_key] = $i_type;
            }
        }

        /* Iterate over failover options */
        foreach ($this->_arr_failover_types as $i_opt) {
            $this->redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $i_opt);

            foreach ($arr_value_ref as $str_key => $value) {
                $this->checkKeyValue($str_key, $arr_type_ref[$str_key], $value);
            }

            break;
        }
    }

    /* Test a 'raw' command */
    public function testRawCommand() {
        $this->redis->rawCommand('mykey', 'set', 'mykey', 'my-value');
        $this->assertEquals($this->redis->get('mykey'), 'my-value');

        $this->redis->del('mylist');
        $this->redis->rpush('mylist', 'A','B','C','D');
        $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']);
    }

    protected function rawCommandArray($key, $args) {
        array_unshift($args, $key);
        return call_user_func_array([$this->redis, 'rawCommand'], $args);
    }

    /* Test that rawCommand and EVAL can be configured to return simple string values */
    public function testReplyLiteral() {
        $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false);
        $this->assertTrue($this->redis->rawCommand('foo', 'set', 'foo', 'bar'));
        $this->assertTrue($this->redis->eval("return redis.call('set', KEYS[1], 'bar')", ['foo'], 1));

        $rv = $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1);
        $this->assertEquals([true, true], $rv);

        $this->redis->setOption(Redis::OPT_REPLY_LITERAL, true);
        $this->assertEquals('OK', $this->redis->rawCommand('foo', 'set', 'foo', 'bar'));
        $this->assertEquals('OK', $this->redis->eval("return redis.call('set', KEYS[1], 'bar')", ['foo'], 1));

        $rv = $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1);
        $this->assertEquals(['OK', 'PONG'], $rv);

        // Reset
        $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false);
    }

    /* Redis and RedisCluster use the same handler for the ACL command but verify we can direct
       the command to a specific node. */
    public function testAcl() {
        if ( ! $this->minVersionCheck("6.0"))
            return $this->markTestSkipped();

        $this->assertInArray('default', $this->redis->acl('foo', 'USERS'));
    }

    public function testSession()
    {
        @ini_set('session.save_handler', 'rediscluster');
        @ini_set('session.save_path', $this->getFullHostPath() . '&failover=error');
        if (!@session_start()) {
            return $this->markTestSkipped();
        }
        session_write_close();
        $this->assertTrue($this->redis->exists('PHPREDIS_CLUSTER_SESSION:' . session_id()));
    }


    /* Test that we are able to use the slot cache without issues */
    public function testSlotCache() {
        ini_set('redis.clusters.cache_slots', 1);

        $pong = 0;
        for ($i = 0; $i < 10; $i++) {
            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth());
            $pong += $obj_rc->ping("key:$i");
        }

        $this->assertEquals($pong, $i);

        ini_set('redis.clusters.cache_slots', 0);
    }

    /* Regression test for connection pool liveness checks */
    public function testConnectionPool() {
        $prev_value = ini_get('redis.pconnect.pooling_enabled');
        ini_set('redis.pconnect.pooling_enabled', 1);

        $pong = 0;
        for ($i = 0; $i < 10; $i++) {
            $obj_rc = new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth());
            $pong += $obj_rc->ping("key:$i");
        }

        $this->assertEquals($pong, $i);
        ini_set('redis.pconnect.pooling_enabled', $prev_value);
    }

    /**
     * @inheritdoc
     */
    protected function getFullHostPath()
    {
        $auth = $this->getAuthFragment();

        return implode('&', array_map(function ($host) {
            return 'seed[]=' . $host;
        }, self::$_arr_node_map)) . ($auth ? "&$auth" : '');
    }

    /* Test correct handling of null multibulk replies */
    public function testNullArray() {
        $key = "key:arr";
        $this->redis->del($key);

        foreach ([false => [], true => NULL] as $opt => $test) {
            $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt);

            $r = $this->redis->rawCommand($key, "BLPOP", $key, .05);
            $this->assertEquals($test, $r);

            $this->redis->multi();
            $this->redis->rawCommand($key, "BLPOP", $key, .05);
            $r = $this->redis->exec();
            $this->assertEquals([$test], $r);
        }

        $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false);
    }
}
?>
PK�[�\8�X99make-cluster.shnu�[���#!/bin/bash

# make-cluster.sh
# This is a simple script used to automatically spin up a Redis cluster instance
# simplifying the process of running unit tests.
#
# Usage:
#   ./make-cluster.sh start [host]
#   ./make-cluster.sh stop [host]
#

BASEDIR=`pwd`
NODEDIR=$BASEDIR/nodes
MAPFILE=$NODEDIR/nodemap

# Host, nodes, replicas, ports, etc.  Change if you want different values
HOST="127.0.0.1"
NOASK=0
NODES=12
REPLICAS=3
START_PORT=7000
END_PORT=`expr $START_PORT + $NODES`

# Helper to determine if we have an executable
checkExe() {
    if ! hash $1 > /dev/null 2>&1; then
        echo "Error:  Must have $1 on the path!"
        exit 1
    fi
}

# Run a command and output what we're running
verboseRun() {
    echo "Running: $@"
    $@
}

# Spawn a specific redis instance, cluster enabled 
spawnNode() {
    # ACL file if we have one
    if [ ! -z "$ACLFILE" ]; then
        ACLARG="--aclfile $ACLFILE"
    fi

    # Attempt to spawn the node
    verboseRun redis-server --cluster-enabled yes --dir $NODEDIR --port $PORT \
        --cluster-config-file node-$PORT.conf --daemonize yes --save \'\' \
        --bind $HOST --dbfilename node-$PORT.rdb $ACLARG

    # Abort if we can't spin this instance
    if [ $? -ne 0 ]; then 
        echo "Error:  Can't spawn node at port $PORT."
        exit 1
    fi
}

# Spawn nodes from start to end port
spawnNodes() {
    for PORT in `seq $START_PORT $END_PORT`; do
        # Attempt to spawn the node
        spawnNode $PORT

        # Add this host:port to our nodemap so the tests can get seeds
        echo "$HOST:$PORT" >> $MAPFILE
    done
}

# Check to see if any nodes are running
checkNodes() {
    echo -n "Checking port availability "
    
    for PORT in `seq $START_PORT $END_PORT`; do
        redis-cli -p $PORT ping > /dev/null 2>&1
        if [ $? -eq 0 ]; then
            echo "FAIL"
            echo "Error:  There appears to be an instance running at port $PORT"
            exit 1
        fi
    done
    
    echo "OK"
}

# Create our 'node' directory if it doesn't exist and clean out any previous
# configuration files from a previous run.
cleanConfigInfo() {
    verboseRun mkdir -p $NODEDIR
    verboseRun rm -f $NODEDIR/*

    if [ -f "$ACLFILE" ]; then
        cp $ACLFILE $NODEDIR/$ACLFILE
    fi
}

# Initialize our cluster with redis-trib.rb
initCluster() {
    TRIBARGS=""
    for PORT in `seq $START_PORT $END_PORT`; do
        TRIBARGS="$TRIBARGS $HOST:$PORT"
    done

    if [[ ! -z "$USER" ]]; then
        USERARG="--user $USER"
    fi
    if [[ ! -z "$PASS" ]]; then
        PASSARG="-a $PASS"
    fi

    if [[ "$1" -eq "1" ]]; then
        echo yes | redis-cli $USERARG $PASSARG -p $START_PORT --cluster create $TRIBARGS --cluster-replicas $REPLICAS
    else
        verboseRun redis-cli $USERARG $PASSARG -p $START_PORT --cluster create $TRIBARGS --cluster-replicas $REPLICAS
    fi

    if [ $? -ne 0 ]; then
        echo "Error:  Couldn't create cluster!"
        exit 1
    fi
}

# Attempt to spin up our cluster
startCluster() {
    # Make sure none of these nodes are already running
    checkNodes

    # Clean out node configuration, etc
    cleanConfigInfo

    # Attempt to spawn the nodes
    spawnNodes

    # Attempt to initialize the cluster
    initCluster $1
}

# Shut down nodes in our cluster
stopCluster() {
    for PORT in `seq $START_PORT $END_PORT`; do
        verboseRun redis-cli -p $PORT SHUTDOWN NOSAVE > /dev/null 2>&1
    done
}

# Shut down nodes by killing them
killCluster() {
    for PORT in `seq $START_PORT $END_PORT`; do
        PID=$(ps aux|grep [r]edis-server|grep $PORT|awk '{print $2}')
        echo -n "Killing $PID: "
        if kill $PID; then
            echo "OK"
        else
            echo "ERROR"
        fi
    done
}

printUsage() {
    echo "Usage: make-cluster [OPTIONS] <start|stop|kill>"
    echo
    echo "  Options"
    echo
    echo "  -u Redis username to use when spawning cluster"
    echo "  -p Redis password to use when spawning cluster"
    echo "  -a Redis acl filename to use when spawning cluster"
    echo "  -y Automatically send 'yes' when starting cluster"
    echo "  -h This message"
    echo
    exit 0
}

# We need redis-server
checkExe redis-server

while getopts "u:p:a:hy" OPT; do
    case $OPT in
        h)
            printUsage
            ;;
        a)
            if [ ! -f "$OPTARG" ]; then
                echo "Error:  '$OPTARG' is not a filename!"
                exit -1
            fi
            ACLFILE=$OPTARG
            ;;
        u)
            USER=$OPTARG
            ;;
        p)
            PASS=$OPTARG
            ;;
        h)
            HOST=$OPTARG
            ;;
        y)
            NOASK=1
            ;;
        *)
            echo "Unknown option: $OPT"
            exit 1
            ;;
    esac
done

shift "$((OPTIND - 1))"

if [[ $# -lt 1 ]]; then
    echo "Error:  Must pass an operation (start or stop)"
    exit -1
fi

case "$1" in
    start)
        startCluster $NOASK
        ;;
    stop)
        stopCluster
        ;;
    kill)
        killCluster
        ;;
    *)
        echo "Usage: make-cluster.sh [options] <start|stop>"
        exit 1
        ;;
esac
PK�[�\�c|rOrORedisArrayTest.phpnu�[���<?php defined('PHPREDIS_TESTRUN') or die("Use TestRedis.php to run tests!\n");
require_once(dirname($_SERVER['PHP_SELF'])."/TestSuite.php");

define('REDIS_ARRAY_DATA_SIZE', 1000);

function custom_hash($str) {
    // str has the following format: $APPID_fb$FACEBOOKID_$key.
    $pos = strpos($str, '_fb');
    if(preg_match("#\w+_fb(?<facebook_id>\d+)_\w+#", $str, $out)) {
            return $out['facebook_id'];
    }
    return $str;
}

function parseHostPort($str, &$host, &$port) {
    $pos = strrpos($str, ':');
    $host = substr($str, 0, $pos);
    $port = substr($str, $pos+1);
}

function getRedisVersion($obj_r) {
    $arr_info = $obj_r->info();
    if (!$arr_info || !isset($arr_info['redis_version'])) {
        return "0.0.0";
    }
    return $arr_info['redis_version'];
}

/* Determine the lowest redis version attached to this RedisArray object */
function getMinVersion($obj_ra) {
    $min_version = "0.0.0";
    foreach ($obj_ra->_hosts() as $host) {
        $version = getRedisVersion($obj_ra->_instance($host));
        if (version_compare($version, $min_version) > 0) {
            $min_version = $version;
        }
    }

    return $min_version;
}

class Redis_Array_Test extends TestSuite
{
    private $min_version;
    private $strings;
    public $ra = NULL;
    private $data = NULL;

    public function setUp() {
        // initialize strings.
        $n = REDIS_ARRAY_DATA_SIZE;
        $this->strings = array();
        for($i = 0; $i < $n; $i++) {
            $this->strings['key-'.$i] = 'val-'.$i;
        }

        global $newRing, $oldRing, $useIndex;
        $options = ['previous' => $oldRing, 'index' => $useIndex];
        if ($this->getAuth()) {
            $options['auth'] = $this->getAuth();
        }
        $this->ra = new RedisArray($newRing, $options);
        $this->min_version = getMinVersion($this->ra);
    }

    public function testMSet() {
        // run mset
        $this->assertTrue(TRUE === $this->ra->mset($this->strings));

        // check each key individually using the array
        foreach($this->strings as $k => $v) {
            $this->assertTrue($v === $this->ra->get($k));
        }

        // check each key individually using a new connection
        foreach($this->strings as $k => $v) {
            parseHostPort($this->ra->_target($k), $host, $port);

            $target = $this->ra->_target($k);
            $pos = strrpos($target, ':');

            $host = substr($target, 0, $pos);
            $port = substr($target, $pos+1);

            $r = new Redis;
            $r->pconnect($host, (int)$port);
            if ($this->getAuth()) {
                $this->assertTrue($r->auth($this->getAuth()));
            }
            $this->assertTrue($v === $r->get($k));
        }
    }

    public function testMGet() {
        $this->assertTrue(array_values($this->strings) === $this->ra->mget(array_keys($this->strings)));
    }

    private function addData($commonString) {
        $this->data = array();
        for($i = 0; $i < REDIS_ARRAY_DATA_SIZE; $i++) {
            $k = rand().'_'.$commonString.'_'.rand();
            $this->data[$k] = rand();
        }
        $this->ra->mset($this->data);
    }

    private function checkCommonLocality() {
        // check that they're all on the same node.
        $lastNode = NULL;
        foreach($this->data as $k => $v) {
                $node = $this->ra->_target($k);
                if($lastNode) {
                    $this->assertTrue($node === $lastNode);
                }
                $this->assertTrue($this->ra->get($k) == $v);
                $lastNode = $node;
        }
    }

    public function testKeyLocality() {

        // basic key locality with default hash
        $this->addData('{hashed part of the key}');
        $this->checkCommonLocality();

        // with common hashing function
        global $newRing, $oldRing, $useIndex;
        $options = ['previous' => $oldRing, 'index' => $useIndex, 'function' => 'custom_hash'];
        if ($this->getAuth()) {
            $options['auth'] = $this->getAuth();
        }
        $this->ra = new RedisArray($newRing, $options);

        // basic key locality with custom hash
        $this->addData('fb'.rand());
        $this->checkCommonLocality();
    }

    public function customDistributor($key)
    {
        $a = unpack("N*", md5($key, true));
        global $newRing;
        $pos = abs($a[1]) % count($newRing);

        return $pos;
    }

    public function testKeyDistributor()
    {
        global $newRing, $useIndex;
        $options = ['index' => $useIndex, 'function' => 'custom_hash', 'distributor' => [$this, "customDistributor"]];
        if ($this->getAuth()) {
            $options['auth'] = $this->getAuth();
        }
        $this->ra = new RedisArray($newRing, $options);

        // custom key distribution function.
        $this->addData('fb'.rand());

        // check that they're all on the expected node.
        $lastNode = NULL;
        foreach($this->data as $k => $v) {
            $node = $this->ra->_target($k);
            $pos = $this->customDistributor($k);
            $this->assertTrue($node === $newRing[$pos]);
        }
    }

    /* Scan a whole key and return the overall result */
    protected function execKeyScan($cmd, $key) {
        $res = [];

        $it = NULL;
        do {
            $chunk = $this->ra->$cmd($key, $it);
            foreach ($chunk as $field => $value) {
                $res[$field] = $value;
            }
        } while ($it !== 0);

        return $res;
    }

    public function testKeyScanning() {
        $h_vals = ['foo' => 'bar', 'baz' => 'bop'];
        $z_vals = ['one' => 1, 'two' => 2, 'three' => 3];
        $s_vals = ['mem1', 'mem2', 'mem3'];

        $this->ra->del(['scan-hash', 'scan-set', 'scan-zset']);

        $this->ra->hMSet('scan-hash', $h_vals);
        foreach ($z_vals as $k => $v)
            $this->ra->zAdd('scan-zset', $v, $k);
        $this->ra->sAdd('scan-set', ...$s_vals);

        $s_scan = $this->execKeyScan('sScan', 'scan-set');
        $this->assertTrue(count(array_diff_key(array_flip($s_vals), array_flip($s_scan))) == 0);

        $this->assertEquals($h_vals, $this->execKeyScan('hScan', 'scan-hash'));

        $z_scan = $this->execKeyScan('zScan', 'scan-zset');
        $this->assertTrue(count($z_scan) == count($z_vals) &&
                          count(array_diff_key($z_vals, $z_scan)) == 0 &&
                          array_sum($z_scan) == array_sum($z_vals));
    }
}

class Redis_Rehashing_Test extends TestSuite
{

    public $ra = NULL;
    private $useIndex;

    private $min_version;

    // data
    private $strings;
    private $sets;
    private $lists;
    private $hashes;
    private $zsets;

    public function setUp() {

        // initialize strings.
        $n = REDIS_ARRAY_DATA_SIZE;
        $this->strings = array();
        for($i = 0; $i < $n; $i++) {
            $this->strings['key-'.$i] = 'val-'.$i;
        }

        // initialize sets
        for($i = 0; $i < $n; $i++) {
            // each set has 20 elements
            $this->sets['set-'.$i] = range($i, $i+20);
        }

        // initialize lists
        for($i = 0; $i < $n; $i++) {
            // each list has 20 elements
            $this->lists['list-'.$i] = range($i, $i+20);
        }

        // initialize hashes
        for($i = 0; $i < $n; $i++) {
            // each hash has 5 keys
            $this->hashes['hash-'.$i] = array('A' => $i, 'B' => $i+1, 'C' => $i+2, 'D' => $i+3, 'E' => $i+4);
        }

        // initialize sorted sets
        for($i = 0; $i < $n; $i++) {
            // each sorted sets has 5 elements
            $this->zsets['zset-'.$i] = array($i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E');
        }

        global $newRing, $oldRing, $useIndex;
        $options = ['previous' => $oldRing, 'index' => $useIndex];
        if ($this->getAuth()) {
            $options['auth'] = $this->getAuth();
        }
        // create array
        $this->ra = new RedisArray($newRing, $options);
        $this->min_version = getMinVersion($this->ra);
    }

    public function testFlush() {
        // flush all servers first.
        global $serverList;
        foreach($serverList as $s) {
            parseHostPort($s, $host, $port);

            $r = new Redis();
            $r->pconnect($host, (int)$port, 0);
            if ($this->getAuth()) {
                $this->assertTrue($r->auth($this->getAuth()));
            }
            $r->flushdb();
        }
    }


    private function distributeKeys() {

        // strings
        foreach($this->strings as $k => $v) {
            $this->ra->set($k, $v);
        }

        // sets
        foreach($this->sets as $k => $v) {
            call_user_func_array(array($this->ra, 'sadd'), array_merge(array($k), $v));
        }

        // lists
        foreach($this->lists as $k => $v) {
            call_user_func_array(array($this->ra, 'rpush'), array_merge(array($k), $v));
        }

        // hashes
        foreach($this->hashes as $k => $v) {
            $this->ra->hmset($k, $v);
        }

        // sorted sets
        foreach($this->zsets as $k => $v) {
            call_user_func_array(array($this->ra, 'zadd'), array_merge(array($k), $v));
        }
    }

    public function testDistribution() {
        $this->distributeKeys();
    }

    public function testSimpleRead() {
        $this->readAllvalues();
    }

    private function readAllvalues() {

        // strings
        foreach($this->strings as $k => $v) {
            $this->assertTrue($this->ra->get($k) === $v);
        }

        // sets
        foreach($this->sets as $k => $v) {
            $ret = $this->ra->smembers($k); // get values

            // sort sets
            sort($v);
            sort($ret);

            $this->assertTrue($ret == $v);
        }

        // lists
        foreach($this->lists as $k => $v) {
            $ret = $this->ra->lrange($k, 0, -1);
            $this->assertTrue($ret == $v);
        }

        // hashes
        foreach($this->hashes as $k => $v) {
            $ret = $this->ra->hgetall($k); // get values
            $this->assertTrue($ret == $v);
        }

        // sorted sets
        foreach($this->zsets as $k => $v) {
            $ret = $this->ra->zrange($k, 0, -1, TRUE); // get values with scores

            // create assoc array from local dataset
            $tmp = array();
            for($i = 0; $i < count($v); $i += 2) {
                $tmp[$v[$i+1]] = $v[$i];
            }

            // compare to RA value
            $this->assertTrue($ret == $tmp);
        }
    }

    // add a new node.
    public function testCreateSecondRing() {

        global $newRing, $oldRing, $serverList;
        $oldRing = $newRing; // back up the original.
        $newRing = $serverList; // add a new node to the main ring.
    }

    public function testReadUsingFallbackMechanism() {
        $this->readAllvalues(); // some of the reads will fail and will go to another target node.
    }

    public function testRehash() {
        $this->ra->_rehash(); // this will redistribute the keys
    }

    public function testRehashWithCallback() {
        $total = 0;
        $this->ra->_rehash(function ($host, $count) use (&$total) {
            $total += $count;
        });
        $this->assertTrue($total > 0);
    }

    public function testReadRedistributedKeys() {
        $this->readAllvalues(); // we shouldn't have any missed reads now.
    }
}

// Test auto-migration of keys
class Redis_Auto_Rehashing_Test extends TestSuite {

    public $ra = NULL;
    private $min_version;

    // data
    private $strings;

    public function setUp() {
        // initialize strings.
        $n = REDIS_ARRAY_DATA_SIZE;
        $this->strings = array();
        for($i = 0; $i < $n; $i++) {
            $this->strings['key-'.$i] = 'val-'.$i;
        }

        global $newRing, $oldRing, $useIndex;
        $options = ['previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE];
        if ($this->getAuth()) {
            $options['auth'] = $this->getAuth();
        }
        // create array
        $this->ra = new RedisArray($newRing, $options);
        $this->min_version = getMinVersion($this->ra);
    }

    public function testDistribute() {
        // strings
        foreach($this->strings as $k => $v) {
            $this->ra->set($k, $v);
        }
    }

    private function readAllvalues() {
        foreach($this->strings as $k => $v) {
            $this->assertTrue($this->ra->get($k) === $v);
        }
    }


    public function testReadAll() {
        $this->readAllvalues();
    }

    // add a new node.
    public function testCreateSecondRing() {
        global $newRing, $oldRing, $serverList;
        $oldRing = $newRing; // back up the original.
        $newRing = $serverList; // add a new node to the main ring.
    }

    // Read and migrate keys on fallback, causing the whole ring to be rehashed.
    public function testReadAndMigrateAll() {
        $this->readAllvalues();
    }

    // Read and migrate keys on fallback, causing the whole ring to be rehashed.
    public function testAllKeysHaveBeenMigrated() {
        foreach($this->strings as $k => $v) {
            parseHostPort($this->ra->_target($k), $host, $port);

            $r = new Redis;
            $r->pconnect($host, $port);
            if ($this->getAuth()) {
                $this->assertTrue($r->auth($this->getAuth()));
            }

            $this->assertTrue($v === $r->get($k));  // check that the key has actually been migrated to the new node.
        }
    }
}

// Test node-specific multi/exec
class Redis_Multi_Exec_Test extends TestSuite {
    public $ra = NULL;
    private $min_version;

    public function setUp() {
        global $newRing, $oldRing, $useIndex;
        $options = ['previous' => $oldRing, 'index' => $useIndex];
        if ($this->getAuth()) {
            $options['auth'] = $this->getAuth();
        }
        // create array
        $this->ra = new RedisArray($newRing, $options);
        $this->min_version = getMinVersion($this->ra);
    }

    public function testInit() {
        $this->ra->set('{groups}:managers', 2);
        $this->ra->set('{groups}:executives', 3);

        $this->ra->set('1_{employee:joe}_name', 'joe');
        $this->ra->set('1_{employee:joe}_group', 2);
        $this->ra->set('1_{employee:joe}_salary', 2000);
    }

    public function testKeyDistribution() {
        // check that all of joe's keys are on the same instance
        $lastNode = NULL;
        foreach(array('name', 'group', 'salary') as $field) {
            $node = $this->ra->_target('1_{employee:joe}_'.$field);
            if($lastNode) {
                $this->assertTrue($node === $lastNode);
            }
            $lastNode = $node;
        }
    }

    public function testMultiExec() {

        // Joe gets a promotion
        $newGroup = $this->ra->get('{groups}:executives');
        $newSalary = 4000;

        // change both in a transaction.
        $host = $this->ra->_target('{employee:joe}');   // transactions are per-node, so we need a reference to it.
        $tr = $this->ra->multi($host)
            ->set('1_{employee:joe}_group', $newGroup)
            ->set('1_{employee:joe}_salary', $newSalary)
            ->exec();

        // check that the group and salary have been changed
        $this->assertTrue($this->ra->get('1_{employee:joe}_group') === $newGroup);
        $this->assertTrue($this->ra->get('1_{employee:joe}_salary') == $newSalary);

    }

    public function testMultiExecMSet() {

        global $newGroup, $newSalary;
        $newGroup = 1;
        $newSalary = 10000;

        // test MSET, making Joe a top-level executive
        $out = $this->ra->multi($this->ra->_target('{employee:joe}'))
                ->mset(array('1_{employee:joe}_group' => $newGroup, '1_{employee:joe}_salary' => $newSalary))
                ->exec();

        $this->assertTrue($out[0] === TRUE);
    }

    public function testMultiExecMGet() {

        global $newGroup, $newSalary;

        // test MGET
        $out = $this->ra->multi($this->ra->_target('{employee:joe}'))
                ->mget(array('1_{employee:joe}_group', '1_{employee:joe}_salary'))
                ->exec();

        $this->assertTrue($out[0][0] == $newGroup);
        $this->assertTrue($out[0][1] == $newSalary);
    }

    public function testMultiExecDel() {

        // test DEL
        $out = $this->ra->multi($this->ra->_target('{employee:joe}'))
            ->del('1_{employee:joe}_group', '1_{employee:joe}_salary')
            ->exec();

        $this->assertTrue($out[0] === 2);
        $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_group'));
        $this->assertEquals(0, $this->ra->exists('1_{employee:joe}_salary'));
    }

    public function testMutliExecUnlink() {
        if (version_compare($this->min_version, "4.0.0", "lt")) {
            $this->markTestSkipped();
        }

        $this->ra->set('{unlink}:key1', 'bar');
        $this->ra->set('{unlink}:key2', 'bar');

        $out = $this->ra->multi($this->ra->_target('{unlink}'))
            ->del('{unlink}:key1', '{unlink}:key2')
            ->exec();

        $this->assertTrue($out[0] === 2);
    }

    public function testDiscard() {
        /* phpredis issue #87 */
        $key = 'test_err';

        $this->assertTrue($this->ra->set($key, 'test'));
        $this->assertTrue('test' === $this->ra->get($key));

        $this->ra->watch($key);

        // After watch, same
        $this->assertTrue('test' === $this->ra->get($key));

        // change in a multi/exec block.
        $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test1')->exec();
        $this->assertTrue($ret === array(true));

        // Get after exec, 'test1':
        $this->assertTrue($this->ra->get($key) === 'test1');

        $this->ra->watch($key);

        // After second watch, still test1.
        $this->assertTrue($this->ra->get($key) === 'test1');

        $ret = $this->ra->multi($this->ra->_target($key))->set($key, 'test2')->discard();
        // Ret after discard: NULL";
        $this->assertTrue($ret === NULL);

        // Get after discard, unchanged:
        $this->assertTrue($this->ra->get($key) === 'test1');
    }

}

// Test custom distribution function
class Redis_Distributor_Test extends TestSuite {

    public $ra = NULL;
    private $min_version;

    public function setUp() {
        global $newRing, $oldRing, $useIndex;
        $options = ['previous' => $oldRing, 'index' => $useIndex, 'distributor' => [$this, 'distribute']];
        if ($this->getAuth()) {
            $options['auth'] = $this->getAuth();
        }
        // create array
        $this->ra = new RedisArray($newRing, $options);
        $this->min_version = getMinVersion($this->ra);
    }

    public function testInit() {
        $this->ra->set('{uk}test', 'joe');
        $this->ra->set('{us}test', 'bob');
    }

    public function distribute($key) {
        $matches = array();
        if (preg_match('/{([^}]+)}.*/', $key, $matches) == 1) {
            $countries = array('uk' => 0, 'us' => 1);
            if (array_key_exists($matches[1], $countries)) {
                return $countries[$matches[1]];
            }
        }
        return 2; // default server
    }

    public function testDistribution() {
        $ukServer = $this->ra->_target('{uk}test');
        $usServer = $this->ra->_target('{us}test');
        $deServer = $this->ra->_target('{de}test');
        $defaultServer = $this->ra->_target('unknown');

        $nodes = $this->ra->_hosts();
        $this->assertTrue($ukServer === $nodes[0]);
        $this->assertTrue($usServer === $nodes[1]);
        $this->assertTrue($deServer === $nodes[2]);
        $this->assertTrue($defaultServer === $nodes[2]);
    }
}

function run_tests($className, $str_filter, $str_host, $auth) {
        // reset rings
        global $newRing, $oldRing, $serverList;

        $newRing = ["$str_host:6379", "$str_host:6380", "$str_host:6381"];
        $oldRing = [];
        $serverList = ["$str_host:6379", "$str_host:6380", "$str_host:6381", "$str_host:6382"];

        // run
        return TestSuite::run($className, $str_filter, $str_host, NULL, $auth);
}

?>
PK�[�\�1DstartSession.phpnu�[���<?php
error_reporting(E_ERROR | E_WARNING);

$redisHost = $argv[1];
$saveHandler = $argv[2];
$sessionId = $argv[3];
$sleepTime = $argv[4];
$maxExecutionTime = $argv[5];
$lock_retries = $argv[6];
$lock_expire = $argv[7];
$sessionData = $argv[8];
$sessionLifetime = $argv[9];

if (empty($redisHost)) {
    $redisHost = 'tcp://localhost:6379';
}

ini_set('session.save_handler', $saveHandler);
ini_set('session.save_path', $redisHost);
ini_set('max_execution_time', $maxExecutionTime);
ini_set('redis.session.lock_retries', $lock_retries);
ini_set('redis.session.lock_expire', $lock_expire);
ini_set('session.gc_maxlifetime', $sessionLifetime);

if (isset($argv[10])) {
    ini_set('redis.session.locking_enabled', $argv[10]);
}

if (isset($argv[11])) {
    ini_set('redis.session.lock_wait_time', $argv[11]);
}

session_id($sessionId);
$sessionStartSuccessful = session_start();
sleep($sleepTime);
if (!empty($sessionData)) {
    $_SESSION['redis_test'] = $sessionData;
}
session_write_close();

echo $sessionStartSuccessful ? 'SUCCESS' : 'FAILURE';
PK�[�\ϟv�&&
TestSuite.phpnu�[���<?php defined('PHPREDIS_TESTRUN') or die("Use TestRedis.php to run tests!\n");

/* A specific exception for when we skip a test */
class TestSkippedException extends Exception {}

// phpunit is such a pain to install, we're going with pure-PHP here.
class TestSuite
{
    /* Host and port the unit tests will use */
    private $str_host;
    private $i_port = 6379;

    /* Redis authentication we'll use */
    private $auth;

    private static $_boo_colorize = false;

    private static $BOLD_ON = "\033[1m";
    private static $BOLD_OFF = "\033[0m";

    private static $BLACK = "\033[0;30m";
    private static $DARKGRAY = "\033[1;30m";
    private static $BLUE = "\033[0;34m";
    private static $PURPLE = "\033[0;35m";
    private static $GREEN = "\033[0;32m";
    private static $YELLOW = "\033[0;33m";
    private static $RED = "\033[0;31m";

    public static $errors = [];
    public static $warnings = [];

    public function __construct($str_host, $i_port, $auth) {
        $this->str_host = $str_host;
        $this->i_port = $i_port;
        $this->auth = $auth;
    }

    public function getHost() { return $this->str_host; }
    public function getPort() { return $this->i_port; }
    public function getAuth() { return $this->auth; }

    public static function getAvailableCompression() {
        $result[] = Redis::COMPRESSION_NONE;
        if (defined('Redis::COMPRESSION_LZF'))
            $result[] = Redis::COMPRESSION_LZF;
        if (defined('Redis::COMPRESSION_LZ4'))
            $result[] = Redis::COMPRESSION_LZ4;
        if (defined('Redis::COMPRESSION_ZSTD'))
            $result[] = Redis::COMPRESSION_ZSTD;

        return $result;
    }

    /**
     * Returns the fully qualified host path,
     * which may be used directly for php.ini parameters like session.save_path
     *
     * @return null|string
     */
    protected function getFullHostPath()
    {
        return $this->str_host
            ? 'tcp://' . $this->str_host . ':' . $this->i_port
            : null;
    }

    public static function make_bold($str_msg) {
        return self::$_boo_colorize
            ? self::$BOLD_ON . $str_msg . self::$BOLD_OFF
            : $str_msg;
    }

    public static function make_success($str_msg) {
        return self::$_boo_colorize
            ? self::$GREEN . $str_msg . self::$BOLD_OFF
            : $str_msg;
    }

    public static function make_fail($str_msg) {
        return self::$_boo_colorize
            ? self::$RED . $str_msg . self::$BOLD_OFF
            : $str_msg;
    }

    public static function make_warning($str_msg) {
        return self::$_boo_colorize
            ? self::$YELLOW . $str_msg . self::$BOLD_OFF
            : $str_msg;
    }

    protected function assertFalse($bool) {
        if(!$bool)
            return true;

        $bt = debug_backtrace(false);
        self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n",
            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);

        return false;
    }

    protected function assertTrue($bool) {
        if($bool)
            return true;

        $bt = debug_backtrace(false);
        self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n",
            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);

        return false;
    }

    protected function assertInArray($ele, $arr, $cb = NULL) {
        if ($cb && !is_callable($cb))
            die("Fatal:  assertInArray callback must be callable!\n");

        if (($in = in_array($ele, $arr)) && (!$cb || $cb($arr[array_search($ele, $arr)])))
            return true;


        $bt = debug_backtrace(false);
        $ex = $in ? 'validation' : 'missing';
        self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n",
            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $ele);

        return false;
    }

    protected function assertArrayKey($arr, $key, $cb = NULL) {
        if ($cb && !is_callable($cb))
            die("Fatal:  assertArrayKey callback must be callable\n");

        if (($exists = isset($arr[$key])) && (!$cb || $cb($arr[$key])))
            return true;

        $bt = debug_backtrace(false);
        $ex = $exists ? 'validation' : 'missing';
        self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s '%s']\n",
            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex, $key);

        return false;
    }

    protected function assertValidate($val, $cb) {
        if ( ! is_callable($cb))
            die("Fatal:  Callable assertValidate callback required\n");

        if ($cb($val))
            return true;

        $bt = debug_backtrace(false);
        self::$errors []= sprintf("Assertion failed: %s:%d (%s)\n",
            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);

        return false;
    }

    protected function assertThrowsMatch($arg, $cb, $regex = NULL) {
        $threw = $match = false;

        if ( ! is_callable($cb))
            die("Fatal:  Callable assertThrows callback required\n");

        try {
            $cb($arg);
        } catch (Exception $ex) {
            $threw = true;
            $match = !$regex || preg_match($regex, $ex->getMessage());
        }

        if ($threw && $match)
            return true;

        $bt = debug_backtrace(false);
        $ex = !$threw ? 'no exception' : "no match '$regex'";
        self::$errors []= sprintf("Assertion failed: %s:%d (%s) [%s]\n",
            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $ex);

        return false;
    }

    protected function assertLess($a, $b) {
        if($a < $b)
            return;

        $bt = debug_backtrace(false);
        self::$errors[] = sprintf("Assertion failed (%s >= %s): %s: %d (%s\n",
            print_r($a, true), print_r($b, true),
            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
    }

    protected function assertEquals($a, $b) {
        if($a === $b)
            return;

        $bt = debug_backtrace(false);
        self::$errors []= sprintf("Assertion failed (%s !== %s): %s:%d (%s)\n",
            print_r($a, true), print_r($b, true),
            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
    }

    protected function assertPatternMatch($str_test, $str_regex) {
        if (preg_match($str_regex, $str_test))
            return;

        $bt = debug_backtrace(false);
        self::$errors []= sprintf("Assertion failed ('%s' doesnt match '%s'): %s:%d (%s)\n",
            $str_test, $str_regex, $bt[0]["file"], $bt[0]["line"], $bt[1]["function"]);
    }

    protected function markTestSkipped($msg='') {
        $bt = debug_backtrace(false);
        self::$warnings []= sprintf("Skipped test: %s:%d (%s) %s\n",
            $bt[0]["file"], $bt[0]["line"], $bt[1]["function"], $msg);

        throw new TestSkippedException($msg);
    }

    private static function getMaxTestLen($arr_methods, $str_limit) {
        $i_result = 0;

        $str_limit = strtolower($str_limit);
        foreach ($arr_methods as $obj_method) {
            $str_name = strtolower($obj_method->name);

            if (substr($str_name, 0, 4) != 'test')
                continue;
            if ($str_limit && !strstr($str_name, $str_limit))
                continue;

            if (strlen($str_name) > $i_result) {
                $i_result = strlen($str_name);
            }
        }
        return $i_result;
    }

    /* Flag colorization */
    public static function flagColorization($boo_override) {
        self::$_boo_colorize = $boo_override && function_exists('posix_isatty') &&
            posix_isatty(STDOUT);
    }

    public static function run($className, $str_limit = NULL, $str_host = NULL, $i_port = NULL, $auth = NULL) {
        /* Lowercase our limit arg if we're passed one */
        $str_limit = $str_limit ? strtolower($str_limit) : $str_limit;

        $rc = new ReflectionClass($className);
        $methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC);

        $i_max_len = self::getMaxTestLen($methods, $str_limit);

        foreach($methods as $m) {
            $name = $m->name;
            if(substr($name, 0, 4) !== 'test')
                continue;

            /* If we're trying to limit to a specific test and can't match the
             * substring, skip */
            if ($str_limit && strstr(strtolower($name), $str_limit)===FALSE) {
                continue;
            }

            $str_out_name = str_pad($name, $i_max_len + 1);
            echo self::make_bold($str_out_name);

            $count = count($className::$errors);
            $rt = new $className($str_host, $i_port, $auth);

            try {
                $rt->setUp();
                $rt->$name();

                if ($count === count($className::$errors)) {
                    $str_msg = self::make_success('PASSED');
                } else {
                    $str_msg = self::make_fail('FAILED');
                }
                //echo ($count === count($className::$errors)) ? "." : "F";
            } catch (Exception $e) {
                /* We may have simply skipped the test */
                if ($e instanceof TestSkippedException) {
                    $str_msg = self::make_warning('SKIPPED');
                } else {
                    $className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n";
                    $str_msg = self::make_fail('FAILED');
                }
            }

            echo "[" . $str_msg . "]\n";
        }
        echo "\n";
        echo implode('', $className::$warnings) . "\n";

        if(empty($className::$errors)) {
            echo "All tests passed. \o/\n";
            return 0;
        }

        echo implode('', $className::$errors);
        return 1;
    }
}

?>
PK�[�\�����	mkring.shnu�[���#!/bin/bash

PORTS="6379 6380 6381 6382"
REDIS=redis-server

function start_node() {
	P=$1
	echo "starting node on port $P";
	CONFIG_FILE=`tempfile`
	cat > $CONFIG_FILE << CONFIG
port $P
CONFIG
	$REDIS $CONFIG_FILE > /dev/null 2>/dev/null &
	sleep 1
	rm -f $CONFIG_FILE
}

function stop_node() {

	P=$1
	PID=$2
	redis-cli -h localhost -p $P shutdown
	kill -9 $PID 2>/dev/null
}

function stop() {
	for P in $PORTS; do
		PID=`lsof -i :$P | tail -1 | cut -f 2 -d " "`
		if [ "$PID" != "" ]; then
			stop_node $P $PID
		fi
	done
}

function start() {
	for P in $PORTS; do
		start_node $P
	done
}

case "$1" in
	start)
		start
		;;
	stop)
		stop
		;;
	restart)
		stop
		start
		;;
	*)
		echo "Usage: $0 [start|stop|restart]"
		;;
esac
PK�[�\?Ƿz00regenerateSessionId.phpnu�[���<?php
error_reporting(E_ERROR | E_WARNING);

$redisHost = $argv[1];
$saveHandler = $argv[2];
$sessionId = $argv[3];
$locking = !!$argv[4];
$destroyPrevious = !!$argv[5];
$sessionProxy = !!$argv[6];

if (empty($redisHost)) {
    $redisHost = 'tcp://localhost:6379';
}

ini_set('session.save_handler', $saveHandler);
ini_set('session.save_path', $redisHost);

if ($locking) {
    ini_set('redis.session.locking_enabled', true);
}

if (interface_exists('SessionHandlerInterface')) {
    class TestHandler implements SessionHandlerInterface
    {
        /**
         * @var SessionHandler
         */
        private $handler;

        public function __construct()
        {
            $this->handler = new SessionHandler();
        }

        public function close()
        {
            return $this->handler->close();
        }

        public function destroy($session_id)
        {
            return $this->handler->destroy($session_id);
        }

        public function gc($maxlifetime)
        {
            return $this->handler->gc($maxlifetime);
        }

        public function open($save_path, $name)
        {
            return $this->handler->open($save_path, $name);
        }

        public function read($session_id)
        {
            return $this->handler->read($session_id);
        }

        public function write($session_id, $session_data)
        {
            return $this->handler->write($session_id, $session_data);
        }
    }
}

if ($sessionProxy) {
    $handler = new TestHandler();
    session_set_save_handler($handler);
}

session_id($sessionId);
if (!session_start()) {
    $result = "FAILED: session_start()";
}
elseif (!session_regenerate_id($destroyPrevious)) {
    $result = "FAILED: session_regenerate_id()";
}
else {
    $result = session_id();
}
session_write_close();
echo $result;

PK�[�\�$�6
6

TestRedis.phpnu�[���<?php define('PHPREDIS_TESTRUN', true);

require_once(dirname($_SERVER['PHP_SELF'])."/TestSuite.php");
require_once(dirname($_SERVER['PHP_SELF'])."/RedisTest.php");
require_once(dirname($_SERVER['PHP_SELF'])."/RedisArrayTest.php");
require_once(dirname($_SERVER['PHP_SELF'])."/RedisClusterTest.php");
require_once(dirname($_SERVER['PHP_SELF'])."/RedisSentinelTest.php");

/* Make sure errors go to stdout and are shown */
error_reporting(E_ALL);
ini_set( 'display_errors','1');

/* Grab options */
$arr_args = getopt('', ['host:', 'port:', 'class:', 'test:', 'nocolors', 'user:', 'auth:']);

/* Grab the test the user is trying to run */
$arr_valid_classes = ['redis', 'redisarray', 'rediscluster', 'redissentinel'];
$str_class = isset($arr_args['class']) ? strtolower($arr_args['class']) : 'redis';
$boo_colorize = !isset($arr_args['nocolors']);

/* Get our test filter if provided one */
$str_filter = isset($arr_args['test']) ? $arr_args['test'] : NULL;

/* Grab override host/port if it was passed */
$str_host = isset($arr_args['host']) ? $arr_args['host'] : '127.0.0.1';
$i_port = isset($arr_args['port']) ? intval($arr_args['port']) : 6379;

/* Get optional username and auth (password) */
$str_user = isset($arr_args['user']) ? $arr_args['user'] : NULL;
$str_auth = isset($arr_args['auth']) ? $arr_args['auth'] : NULL;

/* Massage the actual auth arg */
$auth = NULL;
if ($str_user && $str_auth) {
    $auth = [$str_user, $str_auth];
} else if ($str_auth) {
    $auth = $str_auth;
} else if ($str_user) {
    echo TestSuite::make_warning("User passed without a password, ignoring!\n");
}

/* Validate the class is known */
if (!in_array($str_class, $arr_valid_classes)) {
    echo "Error:  Valid test classes are Redis, RedisArray, RedisCluster and RedisSentinel!\n";
    exit(1);
}

/* Toggle colorization in our TestSuite class */
TestSuite::flagColorization($boo_colorize);

/* Let the user know this can take a bit of time */
echo "Note: these tests might take up to a minute. Don't worry :-)\n";
echo "Using PHP version " . PHP_VERSION . " (" . (PHP_INT_SIZE*8) . " bits)\n";

/* Depending on the classes being tested, run our tests on it */
echo "Testing class ";
if ($str_class == 'redis') {
    echo TestSuite::make_bold("Redis") . "\n";
    exit(TestSuite::run("Redis_Test", $str_filter, $str_host, $i_port, $auth));
} else if ($str_class == 'redisarray') {
    echo TestSuite::make_bold("RedisArray") . "\n";
    global $useIndex;
    foreach(array(true, false) as $useIndex) {
        echo "\n".($useIndex?"WITH":"WITHOUT"). " per-node index:\n";

        /* The various RedisArray subtests we can run */
        $arr_ra_tests = [
            'Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test',
             'Redis_Multi_Exec_Test', 'Redis_Distributor_Test'
        ];

        foreach ($arr_ra_tests as $str_test) {
            /* Run until we encounter a failure */
            if (run_tests($str_test, $str_filter, $str_host, $auth) != 0) {
                exit(1);
            }
        }
    }
} else if ($str_class == 'rediscluster') {
    echo TestSuite::make_bold("RedisCluster") . "\n";
    exit(TestSuite::run("Redis_Cluster_Test", $str_filter, $str_host, $i_port, $auth));
} else {
    echo TestSuite::make_bold("RedisSentinel") . "\n";
    exit(TestSuite::run("Redis_Sentinel_Test", $str_filter, $str_host, $i_port, $auth));
}
?>
PK�[�\Ə-00getSessionData.phpnu�[���<?php
error_reporting(E_ERROR | E_WARNING);

$redisHost = $argv[1];
$saveHandler = $argv[2];
$sessionId = $argv[3];
$sessionLifetime = $argv[4];

if (empty($redisHost)) {
    $redisHost = 'tcp://localhost:6379';
}

ini_set('session.save_handler', $saveHandler);
ini_set('session.save_path', $redisHost);
ini_set('session.gc_maxlifetime', $sessionLifetime);

session_id($sessionId);
if (!session_start()) {
    echo "session_start() was nut successful";
} else {
    echo isset($_SESSION['redis_test']) ? $_SESSION['redis_test'] : 'Key redis_test not found';
}
PK�[�\��@�@
RedisTest.phpnu�[���<?php defined('PHPREDIS_TESTRUN') or die("Use TestRedis.php to run tests!\n");

require_once(dirname($_SERVER['PHP_SELF'])."/TestSuite.php");

class Redis_Test extends TestSuite
{
    /* City lat/long */
    protected $cities = [
        'Chico'         => [-121.837478, 39.728494],
        'Sacramento'    => [-121.494400, 38.581572],
        'Gridley'       => [-121.693583, 39.363777],
        'Marysville'    => [-121.591355, 39.145725],
        'Cupertino'     => [-122.032182, 37.322998]
    ];

    protected $serializers = [
        Redis::SERIALIZER_NONE,
        Redis::SERIALIZER_PHP,
    ];

    /**
     * @var Redis
     */
    public $redis;

    /**
     * @var string
     */
    protected $sessionPrefix = 'PHPREDIS_SESSION:';

    /**
     * @var string
     */
    protected $sessionSaveHandler = 'redis';

    public function setUp() {
        $this->redis = $this->newInstance();
        $info = $this->redis->info();
        $this->version = (isset($info['redis_version'])?$info['redis_version']:'0.0.0');

        if (defined('Redis::SERIALIZER_IGBINARY')) {
            $this->serializers[] = Redis::SERIALIZER_IGBINARY;
        }
    }

    protected function minVersionCheck($version) {
        return version_compare($this->version, $version) >= 0;
    }

    protected function mstime() {
        return round(microtime(true)*1000);
    }

    protected function getAuthParts(&$user, &$pass) {
        $user = $pass = NULL;

        $auth = $this->getAuth();
        if ( ! $auth)
            return;

        if (is_array($auth)) {
            if (count($auth) > 1) {
                list($user, $pass) = $auth;
            } else {
                $pass = $auth[0];
            }
        } else {
            $pass = $auth;
        }
    }

    protected function getAuthFragment() {
        static $_authidx = 0;
        $_authidx++;

        $this->getAuthParts($user, $pass);

        if ($user && $pass) {
            if ($_authidx % 2 == 0)
                return "auth[user]=$user&auth[pass]=$pass";
            else
                return "auth[]=$user&auth[]=$pass";
        } else if ($pass) {
            if ($_authidx % 3 == 0)
                return "auth[pass]=$pass";
            if ($_authidx % 2 == 0)
                return "auth[]=$pass";
            else
                return "auth=$pass";
        } else {
            return NULL;
        }
    }

    protected function getFullHostPath()
    {
        $fullHostPath = parent::getFullHostPath();
        $authFragment = $this->getAuthFragment();

        if (isset($fullHostPath) && $authFragment) {
            $fullHostPath .= "?$authFragment";
        }
        return $fullHostPath;
    }

    protected function newInstance() {
        $r = new Redis();

        $r->connect($this->getHost(), $this->getPort());

        if($this->getAuth()) {
            $this->assertTrue($r->auth($this->getAuth()));
        }
        return $r;
    }

    public function tearDown() {
        if($this->redis) {
            $this->redis->close();
        }
    }

    public function reset()
    {
        $this->setUp();
        $this->tearDown();
    }

    /* Helper function to determine if the clsas has pipeline support */
    protected function havePipeline() {
        $str_constant = get_class($this->redis) . '::PIPELINE';
        return defined($str_constant);
    }

    public function testMinimumVersion()
    {
        // Minimum server version required for tests
        $this->assertTrue(version_compare($this->version, "2.4.0") >= 0);
    }

    public function testPing() {
        /* Reply literal off */
        $this->assertTrue($this->redis->ping());
        $this->assertTrue($this->redis->ping(NULL));
        $this->assertEquals('BEEP', $this->redis->ping('BEEP'));

        /* Make sure we're good in MULTI mode */
        $this->redis->multi();

        $this->redis->ping();
        $this->redis->ping('BEEP');
        $this->assertEquals([true, 'BEEP'], $this->redis->exec());
    }

    public function testPipelinePublish() {
        if (!$this->havePipeline()) {
            $this->markTestSkipped();
        }

        $ret = $this->redis->pipeline()
            ->publish('chan', 'msg')
            ->exec();

        $this->assertTrue(is_array($ret) && count($ret) === 1 && $ret[0] >= 0);
    }

    // Run some simple tests against the PUBSUB command.  This is problematic, as we
    // can't be sure what's going on in the instance, but we can do some things.
    public function testPubSub() {
        // Only available since 2.8.0
        if (version_compare($this->version, "2.8.0") < 0) {
            $this->markTestSkipped();
            return;
        }

        // PUBSUB CHANNELS ...
        $result = $this->redis->pubsub("channels", "*");
        $this->assertTrue(is_array($result));
        $result = $this->redis->pubsub("channels");
        $this->assertTrue(is_array($result));

        // PUBSUB NUMSUB

        $c1 = uniqid() . '-' . rand(1,100);
        $c2 = uniqid() . '-' . rand(1,100);

        $result = $this->redis->pubsub("numsub", [$c1, $c2]);

        // Should get an array back, with two elements
        $this->assertTrue(is_array($result));
        $this->assertEquals(count($result), 2);

        // Make sure the elements are correct, and have zero counts
        foreach([$c1,$c2] as $channel) {
            $this->assertTrue(isset($result[$channel]));
            $this->assertEquals($result[$channel], 0);
        }

        // PUBSUB NUMPAT
        $result = $this->redis->pubsub("numpat");
        $this->assertTrue(is_int($result));

        // Invalid calls
        $this->assertFalse($this->redis->pubsub("notacommand"));
        $this->assertFalse($this->redis->pubsub("numsub", "not-an-array"));
    }

    public function testBitsets() {

        $this->redis->del('key');
        $this->assertEquals(0, $this->redis->getBit('key', 0));
        $this->assertEquals(FALSE, $this->redis->getBit('key', -1));
        $this->assertEquals(0, $this->redis->getBit('key', 100000));

        $this->redis->set('key', "\xff");
        for($i = 0; $i < 8; $i++) {
            $this->assertEquals(1, $this->redis->getBit('key', $i));
        }
        $this->assertEquals(0, $this->redis->getBit('key', 8));

        // change bit 0
        $this->assertEquals(1, $this->redis->setBit('key', 0, 0));
        $this->assertEquals(0, $this->redis->setBit('key', 0, 0));
        $this->assertEquals(0, $this->redis->getBit('key', 0));
        $this->assertEquals("\x7f", $this->redis->get('key'));

        // change bit 1
        $this->assertEquals(1, $this->redis->setBit('key', 1, 0));
        $this->assertEquals(0, $this->redis->setBit('key', 1, 0));
        $this->assertEquals(0, $this->redis->getBit('key', 1));
        $this->assertEquals("\x3f", $this->redis->get('key'));

        // change bit > 1
        $this->assertEquals(1, $this->redis->setBit('key', 2, 0));
        $this->assertEquals(0, $this->redis->setBit('key', 2, 0));
        $this->assertEquals(0, $this->redis->getBit('key', 2));
        $this->assertEquals("\x1f", $this->redis->get('key'));

        // values above 1 are changed to 1 but don't overflow on bits to the right.
        $this->assertEquals(0, $this->redis->setBit('key', 0, 0xff));
        $this->assertEquals("\x9f", $this->redis->get('key'));

        // Verify valid offset ranges
        $this->assertFalse($this->redis->getBit('key', -1));

        $this->redis->setBit('key', 0x7fffffff, 1);
        $this->assertEquals(1, $this->redis->getBit('key', 0x7fffffff));
    }

    public function testBitPos() {
        if (version_compare($this->version, "2.8.7") < 0) {
            $this->MarkTestSkipped();
            return;
        }

        $this->redis->del('bpkey');

        $this->redis->set('bpkey', "\xff\xf0\x00");
        $this->assertEquals($this->redis->bitpos('bpkey', 0), 12);

        $this->redis->set('bpkey', "\x00\xff\xf0");
        $this->assertEquals($this->redis->bitpos('bpkey', 1, 0), 8);
        $this->assertEquals($this->redis->bitpos('bpkey', 1, 1), 8);

        $this->redis->set('bpkey', "\x00\x00\x00");
        $this->assertEquals($this->redis->bitpos('bpkey', 1), -1);
    }

    public function test1000() {

     $s = str_repeat('A', 1000);
     $this->redis->set('x', $s);
     $this->assertEquals($s, $this->redis->get('x'));

     $s = str_repeat('A', 1000000);
     $this->redis->set('x', $s);
     $this->assertEquals($s, $this->redis->get('x'));
    }

    public function testEcho() {
        $this->assertEquals($this->redis->echo("hello"), "hello");
        $this->assertEquals($this->redis->echo(""), "");
        $this->assertEquals($this->redis->echo(" 0123 "), " 0123 ");
    }

    public function testErr() {

     $this->redis->set('x', '-ERR');
     $this->assertEquals($this->redis->get('x'), '-ERR');

    }

    public function testSet()
    {
        $this->assertEquals(TRUE, $this->redis->set('key', 'nil'));
        $this->assertEquals('nil', $this->redis->get('key'));

        $this->assertEquals(TRUE, $this->redis->set('key', 'val'));

        $this->assertEquals('val', $this->redis->get('key'));
        $this->assertEquals('val', $this->redis->get('key'));
        $this->redis->del('keyNotExist');
        $this->assertEquals(FALSE, $this->redis->get('keyNotExist'));

        $this->redis->set('key2', 'val');
        $this->assertEquals('val', $this->redis->get('key2'));

        $value = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';

        $this->redis->set('key2', $value);
        $this->assertEquals($value, $this->redis->get('key2'));
        $this->assertEquals($value, $this->redis->get('key2'));

        $this->redis->del('key');
        $this->redis->del('key2');


        $i = 66000;
        $value2 = 'X';
        while($i--) {
            $value2 .= 'A';
        }
        $value2 .= 'X';

        $this->redis->set('key', $value2);
        $this->assertEquals($value2, $this->redis->get('key'));
        $this->redis->del('key');
        $this->assertEquals(False, $this->redis->get('key'));

        $data = gzcompress('42');
        $this->assertEquals(True, $this->redis->set('key', $data));
        $this->assertEquals('42', gzuncompress($this->redis->get('key')));

        $this->redis->del('key');
        $data = gzcompress('value1');
        $this->assertEquals(True, $this->redis->set('key', $data));
        $this->assertEquals('value1', gzuncompress($this->redis->get('key')));

        $this->redis->del('key');
        $this->assertEquals(TRUE, $this->redis->set('key', 0));
        $this->assertEquals('0', $this->redis->get('key'));
        $this->assertEquals(TRUE, $this->redis->set('key', 1));
        $this->assertEquals('1', $this->redis->get('key'));
        $this->assertEquals(TRUE, $this->redis->set('key', 0.1));
        $this->assertEquals('0.1', $this->redis->get('key'));
        $this->assertEquals(TRUE, $this->redis->set('key', '0.1'));
        $this->assertEquals('0.1', $this->redis->get('key'));
        $this->assertEquals(TRUE, $this->redis->set('key', TRUE));
        $this->assertEquals('1', $this->redis->get('key'));

        $this->assertEquals(True, $this->redis->set('key', ''));
        $this->assertEquals('', $this->redis->get('key'));
        $this->assertEquals(True, $this->redis->set('key', NULL));
        $this->assertEquals('', $this->redis->get('key'));

        $this->assertEquals(True, $this->redis->set('key', gzcompress('42')));
        $this->assertEquals('42', gzuncompress($this->redis->get('key')));
    }

    /* Extended SET options for Redis >= 2.6.12 */
    public function testExtendedSet() {
        // Skip the test if we don't have a new enough version of Redis
        if (version_compare($this->version, '2.6.12') < 0) {
            $this->markTestSkipped();
            return;
        }

        /* Legacy SETEX redirection */
        $this->redis->del('foo');
        $this->assertTrue($this->redis->set('foo','bar', 20));
        $this->assertEquals($this->redis->get('foo'), 'bar');
        $this->assertEquals($this->redis->ttl('foo'), 20);

        /* Should coerce doubles into long */
        $this->assertTrue($this->redis->set('foo', 'bar-20.5', 20.5));
        $this->assertEquals($this->redis->ttl('foo'), 20);
        $this->assertEquals($this->redis->get('foo'), 'bar-20.5');

        /* Invalid third arguments */
        $this->assertFalse(@$this->redis->set('foo','bar','baz'));
        $this->assertFalse(@$this->redis->set('foo','bar',new StdClass()));

        /* Set if not exist */
        $this->redis->del('foo');
        $this->assertTrue($this->redis->set('foo','bar', ['nx']));
        $this->assertEquals($this->redis->get('foo'), 'bar');
        $this->assertFalse($this->redis->set('foo','bar', ['nx']));

        /* Set if exists */
        $this->assertTrue($this->redis->set('foo','bar', ['xx']));
        $this->assertEquals($this->redis->get('foo'), 'bar');
        $this->redis->del('foo');
        $this->assertFalse($this->redis->set('foo','bar', ['xx']));

        /* Set with a TTL */
        $this->assertTrue($this->redis->set('foo','bar', ['ex'=>100]));
        $this->assertEquals($this->redis->ttl('foo'), 100);

        /* Set with a PTTL */
        $this->assertTrue($this->redis->set('foo','bar',['px'=>100000]));
        $this->assertTrue(100000 - $this->redis->pttl('foo') < 1000);

        /* Set if exists, with a TTL */
        $this->assertTrue($this->redis->set('foo','bar',['xx','ex'=>105]));
        $this->assertEquals($this->redis->ttl('foo'), 105);
        $this->assertEquals($this->redis->get('foo'), 'bar');

        /* Set if not exists, with a TTL */
        $this->redis->del('foo');
        $this->assertTrue($this->redis->set('foo','bar', ['nx', 'ex'=>110]));
        $this->assertEquals($this->redis->ttl('foo'), 110);
        $this->assertEquals($this->redis->get('foo'), 'bar');
        $this->assertFalse($this->redis->set('foo','bar', ['nx', 'ex'=>110]));

        /* Throw some nonsense into the array, and check that the TTL came through */
        $this->redis->del('foo');
        $this->assertTrue($this->redis->set('foo','barbaz', ['not-valid','nx','invalid','ex'=>200]));
        $this->assertEquals($this->redis->ttl('foo'), 200);
        $this->assertEquals($this->redis->get('foo'), 'barbaz');

        /* Pass NULL as the optional arguments which should be ignored */
        $this->redis->del('foo');
        $this->redis->set('foo','bar', NULL);
        $this->assertEquals($this->redis->get('foo'), 'bar');
        $this->assertTrue($this->redis->ttl('foo')<0);

        /* Make sure we ignore bad/non-string options (regression test for #1835) */
        $this->assertTrue($this->redis->set('foo', 'bar', [NULL, 'EX' => 60]));
        $this->assertTrue($this->redis->set('foo', 'bar', [NULL, new stdClass(), 'EX' => 60]));
        $this->assertFalse(@$this->redis->set('foo', 'bar', [NULL, 'EX' => []]));

        if (version_compare($this->version, "6.0.0") < 0)
            return;

        /* KEEPTTL works by itself */
        $this->redis->set('foo', 'bar', ['EX' => 100]);
        $this->redis->set('foo', 'bar', ['KEEPTTL']);
        $this->assertTrue($this->redis->ttl('foo') > -1);

        /* Works with other options */
        $this->redis->set('foo', 'bar', ['XX', 'KEEPTTL']);
        $this->assertTrue($this->redis->ttl('foo') > -1);
        $this->redis->set('foo', 'bar', ['XX']);
        $this->assertTrue($this->redis->ttl('foo') == -1);
    }

    public function testGetSet() {
        $this->redis->del('key');
        $this->assertTrue($this->redis->getSet('key', '42') === FALSE);
        $this->assertTrue($this->redis->getSet('key', '123') === '42');
        $this->assertTrue($this->redis->getSet('key', '123') === '123');
    }

    public function testRandomKey() {
        for($i = 0; $i < 1000; $i++) {
            $k = $this->redis->randomKey();
            $this->assertEquals($this->redis->exists($k), 1);
        }
    }

    public function testRename() {
        // strings
        $this->redis->del('{key}0');
        $this->redis->set('{key}0', 'val0');
        $this->redis->rename('{key}0', '{key}1');
        $this->assertEquals(FALSE, $this->redis->get('{key}0'));
        $this->assertEquals('val0', $this->redis->get('{key}1'));
    }

    public function testRenameNx() {
        // strings
        $this->redis->del('{key}0', '{key}1');
        $this->redis->set('{key}0', 'val0');
        $this->redis->set('{key}1', 'val1');
        $this->assertTrue($this->redis->renameNx('{key}0', '{key}1') === FALSE);
        $this->assertTrue($this->redis->get('{key}0') === 'val0');
        $this->assertTrue($this->redis->get('{key}1') === 'val1');

        // lists
        $this->redis->del('{key}0');
        $this->redis->del('{key}1');
        $this->redis->lPush('{key}0', 'val0');
        $this->redis->lPush('{key}0', 'val1');
        $this->redis->lPush('{key}1', 'val1-0');
        $this->redis->lPush('{key}1', 'val1-1');
        $this->assertTrue($this->redis->renameNx('{key}0', '{key}1') === FALSE);
        $this->assertTrue($this->redis->lRange('{key}0', 0, -1) === ['val1', 'val0']);
        $this->assertTrue($this->redis->lRange('{key}1', 0, -1) === ['val1-1', 'val1-0']);

        $this->redis->del('{key}2');
        $this->assertTrue($this->redis->renameNx('{key}0', '{key}2') === TRUE);
        $this->assertTrue($this->redis->lRange('{key}0', 0, -1) === []);
        $this->assertTrue($this->redis->lRange('{key}2', 0, -1) === ['val1', 'val0']);
    }

    public function testMultiple() {
        $this->redis->del('k1');
        $this->redis->del('k2');
        $this->redis->del('k3');

        $this->redis->set('k1', 'v1');
        $this->redis->set('k2', 'v2');
        $this->redis->set('k3', 'v3');
        $this->redis->set(1, 'test');

        $this->assertEquals(['v1'], $this->redis->mget(['k1']));
        $this->assertEquals(['v1', 'v3', false], $this->redis->mget(['k1', 'k3', 'NoKey']));
        $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['k1', 'k2', 'k3']));
        $this->assertEquals(['v1', 'v2', 'v3'], $this->redis->mget(['k1', 'k2', 'k3']));

        $this->redis->set('k5', '$1111111111');
        $this->assertEquals([0 => '$1111111111'], $this->redis->mget(['k5']));

        $this->assertEquals([0 => 'test'], $this->redis->mget([1])); // non-string
    }

    public function testMultipleBin() {

    $this->redis->del('k1');
        $this->redis->del('k2');
        $this->redis->del('k3');

        $this->redis->set('k1', gzcompress('v1'));
        $this->redis->set('k2', gzcompress('v2'));
        $this->redis->set('k3', gzcompress('v3'));

        $this->assertEquals([gzcompress('v1'), gzcompress('v2'), gzcompress('v3')], $this->redis->mget(['k1', 'k2', 'k3']));
        $this->assertEquals([gzcompress('v1'), gzcompress('v2'), gzcompress('v3')], $this->redis->mget(['k1', 'k2', 'k3']));

    }

    public function testSetTimeout() {
        $this->redis->del('key');
        $this->redis->set('key', 'value');
        $this->assertEquals('value', $this->redis->get('key'));
        $this->redis->expire('key', 1);
        $this->assertEquals('value', $this->redis->get('key'));
        sleep(2);
        $this->assertEquals(False, $this->redis->get('key'));
    }

    /* This test is prone to failure in the Travis container, so attempt to mitigate this by running more than once */
    public function testExpireAt() {
        $success = false;

        for ($i = 0; !$success && $i < 3; $i++) {
            $this->redis->del('key');
            $this->redis->set('key', 'value');
            $this->redis->expireAt('key', time() + 1);
            usleep(1500000);
            $success = FALSE === $this->redis->get('key');
        }

        $this->assertTrue($success);
    }

    public function testSetEx() {

        $this->redis->del('key');
        $this->assertTrue($this->redis->setex('key', 7, 'val') === TRUE);
        $this->assertTrue($this->redis->ttl('key') ===7);
        $this->assertTrue($this->redis->get('key') === 'val');
    }

    public function testPSetEx() {
        $this->redis->del('key');
        $this->assertTrue($this->redis->psetex('key', 7 * 1000, 'val') === TRUE);
        $this->assertTrue($this->redis->ttl('key') ===7);
        $this->assertTrue($this->redis->get('key') === 'val');
    }

    public function testSetNX() {

        $this->redis->set('key', 42);
        $this->assertTrue($this->redis->setnx('key', 'err') === FALSE);
        $this->assertTrue($this->redis->get('key') === '42');

        $this->redis->del('key');
        $this->assertTrue($this->redis->setnx('key', '42') === TRUE);
        $this->assertTrue($this->redis->get('key') === '42');
    }

    public function testExpireAtWithLong() {
        if (PHP_INT_SIZE != 8) {
            $this->markTestSkipped('64 bits only');
        }
        $longExpiryTimeExceedingInt = 3153600000;
        $this->redis->del('key');
        $this->assertTrue($this->redis->setex('key', $longExpiryTimeExceedingInt, 'val') === TRUE);
        $this->assertTrue($this->redis->ttl('key') === $longExpiryTimeExceedingInt);
    }

    public function testIncr()
    {
        $this->redis->set('key', 0);

        $this->redis->incr('key');
        $this->assertEquals(1, (int)$this->redis->get('key'));

        $this->redis->incr('key');
        $this->assertEquals(2, (int)$this->redis->get('key'));

        $this->redis->incrBy('key', 3);
        $this->assertEquals(5, (int)$this->redis->get('key'));

        $this->redis->incrBy('key', 1);
        $this->assertEquals(6, (int)$this->redis->get('key'));

        $this->redis->incrBy('key', -1);
        $this->assertEquals(5, (int)$this->redis->get('key'));

        $this->redis->incr('key', 5);
        $this->assertEquals(10, (int)$this->redis->get('key'));

        $this->redis->del('key');

        $this->redis->set('key', 'abc');

        $this->redis->incr('key');
        $this->assertTrue("abc" === $this->redis->get('key'));

        $this->redis->incr('key');
        $this->assertTrue("abc" === $this->redis->get('key'));

        $this->redis->set('key', 0);
        $this->assertEquals(PHP_INT_MAX, $this->redis->incrby('key', PHP_INT_MAX));
    }

    public function testIncrByFloat()
    {
        // incrbyfloat is new in 2.6.0
        if (version_compare($this->version, "2.5.0") < 0) {
            $this->markTestSkipped();
        }

        $this->redis->del('key');

        $this->redis->set('key', 0);

        $this->redis->incrbyfloat('key', 1.5);
        $this->assertEquals('1.5', $this->redis->get('key'));

        $this->redis->incrbyfloat('key', 2.25);
        $this->assertEquals('3.75', $this->redis->get('key'));

        $this->redis->incrbyfloat('key', -2.25);
        $this->assertEquals('1.5', $this->redis->get('key'));

        $this->redis->set('key', 'abc');

        $this->redis->incrbyfloat('key', 1.5);
        $this->assertTrue("abc" === $this->redis->get('key'));

        $this->redis->incrbyfloat('key', -1.5);
        $this->assertTrue("abc" === $this->redis->get('key'));

        // Test with prefixing
        $this->redis->setOption(Redis::OPT_PREFIX, 'someprefix:');
        $this->redis->del('key');
        $this->redis->incrbyfloat('key',1.8);
        $this->assertEquals(1.8, floatval($this->redis->get('key'))); // convert to float to avoid rounding issue on arm
        $this->redis->setOption(Redis::OPT_PREFIX, '');
        $this->assertEquals(1, $this->redis->exists('someprefix:key'));
        $this->redis->del('someprefix:key');

    }

    public function testDecr()
    {
        $this->redis->set('key', 5);

        $this->redis->decr('key');
        $this->assertEquals(4, (int)$this->redis->get('key'));

        $this->redis->decr('key');
        $this->assertEquals(3, (int)$this->redis->get('key'));

        $this->redis->decrBy('key', 2);
        $this->assertEquals(1, (int)$this->redis->get('key'));

        $this->redis->decrBy('key', 1);
        $this->assertEquals(0, (int)$this->redis->get('key'));

        $this->redis->decrBy('key', -10);
        $this->assertEquals(10, (int)$this->redis->get('key'));

        $this->redis->decr('key', 10);
        $this->assertEquals(0, (int)$this->redis->get('key'));
    }


    public function testExists()
    {
        /* Single key */
        $this->redis->del('key');
        $this->assertEquals(0, $this->redis->exists('key'));
        $this->redis->set('key', 'val');
        $this->assertEquals(1, $this->redis->exists('key'));

        /* Add multiple keys */
        $mkeys = [];
        for ($i = 0; $i < 10; $i++) {
            if (rand(1, 2) == 1) {
                $mkey = "{exists}key:$i";
                $this->redis->set($mkey, $i);
                $mkeys[] = $mkey;
            }
        }

        /* Test passing an array as well as the keys variadic */
        $this->assertEquals(count($mkeys), $this->redis->exists($mkeys));
        $this->assertEquals(count($mkeys), call_user_func_array([$this->redis, 'exists'], $mkeys));
    }

    public function testKeys()
    {
        $pattern = 'keys-test-';
        for($i = 1; $i < 10; $i++) {
            $this->redis->set($pattern.$i, $i);
        }
        $this->redis->del($pattern.'3');
        $keys = $this->redis->keys($pattern.'*');

        $this->redis->set($pattern.'3', 'something');

        $keys2 = $this->redis->keys($pattern.'*');

        $this->assertEquals((count($keys) + 1), count($keys2));

        // empty array when no key matches
        $this->assertEquals([], $this->redis->keys(rand().rand().rand().'*'));
    }

    protected function genericDelUnlink($cmd) {
        $key = 'key' . rand();
        $this->redis->set($key, 'val');
        $this->assertEquals('val', $this->redis->get($key));
        $this->assertEquals(1, $this->redis->$cmd($key));
        $this->assertEquals(false, $this->redis->get($key));

        // multiple, all existing
        $this->redis->set('x', 0);
        $this->redis->set('y', 1);
        $this->redis->set('z', 2);
        $this->assertEquals(3, $this->redis->$cmd('x', 'y', 'z'));
        $this->assertEquals(false, $this->redis->get('x'));
        $this->assertEquals(false, $this->redis->get('y'));
        $this->assertEquals(false, $this->redis->get('z'));

        // multiple, none existing
        $this->assertEquals(0, $this->redis->$cmd('x', 'y', 'z'));
        $this->assertEquals(false, $this->redis->get('x'));
        $this->assertEquals(false, $this->redis->get('y'));
        $this->assertEquals(false, $this->redis->get('z'));

        // multiple, some existing
        $this->redis->set('y', 1);
        $this->assertEquals(1, $this->redis->$cmd('x', 'y', 'z'));
        $this->assertEquals(false, $this->redis->get('y'));

        $this->redis->set('x', 0);
        $this->redis->set('y', 1);
        $this->assertEquals(2, $this->redis->$cmd(['x', 'y']));
    }

    public function testDelete() {
        $this->genericDelUnlink("DEL");
    }

    public function testUnlink() {
        if (version_compare($this->version, "4.0.0") < 0) {
            $this->markTestSkipped();
            return;
        }

        $this->genericDelUnlink("UNLINK");
    }

    public function testType()
    {
        // 0 => none, (key didn't exist)
        // 1=> string,
        // 2 => set,
        // 3 => list,
        // 4 => zset,
        // 5 => hash
        // 6 => stream

        // string
        $this->redis->set('key', 'val');
        $this->assertEquals(Redis::REDIS_STRING, $this->redis->type('key'));

        // list
        $this->redis->lPush('keyList', 'val0');
        $this->redis->lPush('keyList', 'val1');
        $this->assertEquals(Redis::REDIS_LIST, $this->redis->type('keyList'));

        // set
        $this->redis->del('keySet');
        $this->redis->sAdd('keySet', 'val0');
        $this->redis->sAdd('keySet', 'val1');
        $this->assertEquals(Redis::REDIS_SET, $this->redis->type('keySet'));

        // sadd with numeric key
        $this->redis->del(123);
        $this->assertTrue(1 === $this->redis->sAdd(123, 'val0'));
        $this->assertTrue(['val0'] === $this->redis->sMembers(123));

        // zset
        $this->redis->del('keyZSet');
        $this->redis->zAdd('keyZSet', 0, 'val0');
        $this->redis->zAdd('keyZSet', 1, 'val1');
        $this->assertEquals(Redis::REDIS_ZSET, $this->redis->type('keyZSet'));

        // hash
        $this->redis->del('keyHash');
        $this->redis->hSet('keyHash', 'key0', 'val0');
        $this->redis->hSet('keyHash', 'key1', 'val1');
        $this->assertEquals(Redis::REDIS_HASH, $this->redis->type('keyHash'));

        // stream
        if ($this->minVersionCheck("5.0")) {
            $this->redis->del('stream');
            $this->redis->xAdd('stream', '*', ['foo' => 'bar']);
            $this->assertEquals(Redis::REDIS_STREAM, $this->redis->type('stream'));
        }

        // None
        $this->redis->del('keyNotExists');
        $this->assertEquals(Redis::REDIS_NOT_FOUND, $this->redis->type('keyNotExists'));

    }

    public function testStr() {

        $this->redis->set('key', 'val1');
        $this->assertTrue($this->redis->append('key', 'val2') === 8);
        $this->assertTrue($this->redis->get('key') === 'val1val2');

        $this->redis->del('keyNotExist');
        $this->assertTrue($this->redis->append('keyNotExist', 'value') === 5);
        $this->assertTrue($this->redis->get('keyNotExist') === 'value');

        $this->redis->set('key', 'This is a string') ;
        $this->assertTrue($this->redis->getRange('key', 0, 3) === 'This');
        $this->assertTrue($this->redis->getRange('key', -6, -1) === 'string');
        $this->assertTrue($this->redis->getRange('key', -6, 100000) === 'string');
        $this->assertTrue($this->redis->get('key') === 'This is a string');

        $this->redis->set('key', 'This is a string') ;
        $this->assertTrue($this->redis->strlen('key') === 16);

        $this->redis->set('key', 10) ;
        $this->assertTrue($this->redis->strlen('key') === 2);
        $this->redis->set('key', '') ;
        $this->assertTrue($this->redis->strlen('key') === 0);
        $this->redis->set('key', '000') ;
        $this->assertTrue($this->redis->strlen('key') === 3);
    }

    // PUSH, POP : LPUSH, LPOP
    public function testlPop()
    {

    //  rpush  => tail
    //  lpush => head


        $this->redis->del('list');

        $this->redis->lPush('list', 'val');
        $this->redis->lPush('list', 'val2');
    $this->redis->rPush('list', 'val3');

    // 'list' = [ 'val2', 'val', 'val3']

    $this->assertEquals('val2', $this->redis->lPop('list'));
        $this->assertEquals('val', $this->redis->lPop('list'));
        $this->assertEquals('val3', $this->redis->lPop('list'));
        $this->assertEquals(FALSE, $this->redis->lPop('list'));

    // testing binary data

    $this->redis->del('list');
    $this->assertEquals(1, $this->redis->lPush('list', gzcompress('val1')));
    $this->assertEquals(2, $this->redis->lPush('list', gzcompress('val2')));
    $this->assertEquals(3, $this->redis->lPush('list', gzcompress('val3')));

    $this->assertEquals('val3', gzuncompress($this->redis->lPop('list')));
    $this->assertEquals('val2', gzuncompress($this->redis->lPop('list')));
    $this->assertEquals('val1', gzuncompress($this->redis->lPop('list')));

    }

    // PUSH, POP : RPUSH, RPOP
    public function testrPop()
    {
    //  rpush  => tail
    //  lpush => head

        $this->redis->del('list');

        $this->redis->rPush('list', 'val');
        $this->redis->rPush('list', 'val2');
    $this->redis->lPush('list', 'val3');

    // 'list' = [ 'val3', 'val', 'val2']

    $this->assertEquals('val2', $this->redis->rPop('list'));
        $this->assertEquals('val', $this->redis->rPop('list'));
        $this->assertEquals('val3', $this->redis->rPop('list'));
        $this->assertEquals(FALSE, $this->redis->rPop('list'));

    // testing binary data

    $this->redis->del('list');
    $this->assertEquals(1, $this->redis->rPush('list', gzcompress('val1')));
    $this->assertEquals(2, $this->redis->rPush('list', gzcompress('val2')));
    $this->assertEquals(3, $this->redis->rPush('list', gzcompress('val3')));

    $this->assertEquals('val3', gzuncompress($this->redis->rPop('list')));
    $this->assertEquals('val2', gzuncompress($this->redis->rPop('list')));
    $this->assertEquals('val1', gzuncompress($this->redis->rPop('list')));

    }

    public function testblockingPop() {
        // non blocking blPop, brPop
        $this->redis->del('list');
        $this->redis->lPush('list', 'val1');
        $this->redis->lPush('list', 'val2');
        $this->assertTrue($this->redis->blPop(['list'], 2) === ['list', 'val2']);
        $this->assertTrue($this->redis->blPop(['list'], 2) === ['list', 'val1']);

        $this->redis->del('list');
        $this->redis->lPush('list', 'val1');
        $this->redis->lPush('list', 'val2');
        $this->assertTrue($this->redis->brPop(['list'], 1) === ['list', 'val1']);
        $this->assertTrue($this->redis->brPop(['list'], 1) === ['list', 'val2']);

        // blocking blpop, brpop
        $this->redis->del('list');

        /* Also test our option that we want *-1 to be returned as NULL */
        foreach ([false => [], true => NULL] as $opt => $val) {
            $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt);
            $this->assertEquals($val, $this->redis->blPop(['list'], 1));
            $this->assertEquals($val, $this->redis->brPop(['list'], 1));
        }

        $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false);
    }

    public function testllen()
    {
        $this->redis->del('list');

        $this->redis->lPush('list', 'val');
        $this->assertEquals(1, $this->redis->llen('list'));

        $this->redis->lPush('list', 'val2');
        $this->assertEquals(2, $this->redis->llen('list'));

        $this->assertEquals('val2', $this->redis->lPop('list'));
        $this->assertEquals(1, $this->redis->llen('list'));

        $this->assertEquals('val', $this->redis->lPop('list'));
        $this->assertEquals(0, $this->redis->llen('list'));

        $this->assertEquals(FALSE, $this->redis->lPop('list'));
        $this->assertEquals(0, $this->redis->llen('list'));    // empty returns 0

        $this->redis->del('list');
        $this->assertEquals(0, $this->redis->llen('list'));    // non-existent returns 0

        $this->redis->set('list', 'actually not a list');
        $this->assertEquals(FALSE, $this->redis->llen('list'));// not a list returns FALSE
    }

    //lInsert, lPopx, rPopx
    public function testlPopx() {
        //test lPushx/rPushx
        $this->redis->del('keyNotExists');
        $this->assertTrue($this->redis->lPushx('keyNotExists', 'value') === 0);
        $this->assertTrue($this->redis->rPushx('keyNotExists', 'value') === 0);

        $this->redis->del('key');
        $this->redis->lPush('key', 'val0');
        $this->assertTrue($this->redis->lPushx('key', 'val1') === 2);
        $this->assertTrue($this->redis->rPushx('key', 'val2') === 3);
        $this->assertTrue($this->redis->lrange('key', 0, -1) === ['val1', 'val0', 'val2']);

        //test linsert
        $this->redis->del('key');
        $this->redis->lPush('key', 'val0');
        $this->assertTrue($this->redis->lInsert('keyNotExists', Redis::AFTER, 'val1', 'val2') === 0);
        $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, 'valX', 'val2') === -1);

        $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, 'val0', 'val1') === 2);
        $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, 'val0', 'val2') === 3);
        $this->assertTrue($this->redis->lrange('key', 0, -1) === ['val2', 'val0', 'val1']);
    }

    // ltrim, lsize, lpop
    public function testltrim()
    {

        $this->redis->del('list');

        $this->redis->lPush('list', 'val');
        $this->redis->lPush('list', 'val2');
        $this->redis->lPush('list', 'val3');
        $this->redis->lPush('list', 'val4');

    $this->assertEquals(TRUE, $this->redis->ltrim('list', 0, 2));
    $this->assertEquals(3, $this->redis->llen('list'));

        $this->redis->ltrim('list', 0, 0);
        $this->assertEquals(1, $this->redis->llen('list'));
    $this->assertEquals('val4', $this->redis->lPop('list'));

    $this->assertEquals(TRUE, $this->redis->ltrim('list', 10, 10000));
    $this->assertEquals(TRUE, $this->redis->ltrim('list', 10000, 10));

    // test invalid type
    $this->redis->set('list', 'not a list...');
    $this->assertEquals(FALSE, $this->redis->ltrim('list', 0, 2));

    }

    public function setupSort() {
        // people with name, age, salary
        $this->redis->set('person:name_1', 'Alice');
        $this->redis->set('person:age_1', 27);
        $this->redis->set('person:salary_1', 2500);

        $this->redis->set('person:name_2', 'Bob');
        $this->redis->set('person:age_2', 34);
        $this->redis->set('person:salary_2', 2000);

        $this->redis->set('person:name_3', 'Carol');
        $this->redis->set('person:age_3', 25);
        $this->redis->set('person:salary_3', 2800);

        $this->redis->set('person:name_4', 'Dave');
        $this->redis->set('person:age_4', 41);
        $this->redis->set('person:salary_4', 3100);

        // set-up
        $this->redis->del('person:id');
        foreach([1,2,3,4] as $id) {
            $this->redis->lPush('person:id', $id);
        }
    }

    public function testSortPrefix() {
        // Make sure that sorting works with a prefix
        $this->redis->setOption(Redis::OPT_PREFIX, 'some-prefix:');
        $this->redis->del('some-item');
        $this->redis->sadd('some-item', 1);
        $this->redis->sadd('some-item', 2);
        $this->redis->sadd('some-item', 3);

        $this->assertEquals(['1','2','3'], $this->redis->sort('some-item', ['sort' => 'asc']));
        $this->assertEquals(['3','2','1'], $this->redis->sort('some-item', ['sort' => 'desc']));
        $this->assertEquals(['1','2','3'], $this->redis->sort('some-item'));

        // Kill our set/prefix
        $this->redis->del('some-item');
        $this->redis->setOption(Redis::OPT_PREFIX, '');
    }

    public function testSortAsc() {
        $this->setupSort();
        // sort by age and get IDs
        $byAgeAsc = ['3','1','2','4'];
        $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*']));
        $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'sort' => 'asc']));
        $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['by' => NULL]));   // check that NULL works.
        $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['by' => NULL, 'get' => NULL])); // for all fields.
        $this->assertEquals(['1', '2', '3', '4'], $this->redis->sort('person:id', ['sort' => 'asc']));

        // sort by age and get names
        $byAgeAsc = ['Carol','Alice','Bob','Dave'];
        $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*']));
        $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'asc']));

        $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2]]));
        $this->assertEquals(array_slice($byAgeAsc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2], 'sort' => 'asc']));

        $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2]]));
        $this->assertEquals(array_slice($byAgeAsc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'asc']));
        $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 4]]));
        $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, "4"]])); // with strings
        $this->assertEquals($byAgeAsc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => ["0", 4]]));

        // sort by salary and get ages
        $agesBySalaryAsc = ['34', '27', '25', '41'];
        $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', ['by' => 'person:salary_*', 'get' => 'person:age_*']));
        $this->assertEquals($agesBySalaryAsc, $this->redis->sort('person:id', ['by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'asc']));

        $agesAndSalaries = $this->redis->sort('person:id', ['by' => 'person:salary_*', 'get' => ['person:age_*', 'person:salary_*'], 'sort' => 'asc']);
        $this->assertEquals(['34', '2000', '27', '2500', '25', '2800', '41', '3100'], $agesAndSalaries);

        // sort non-alpha doesn't change all-string lists
        // list → [ghi, def, abc]
        $list = ['abc', 'def', 'ghi'];
        $this->redis->del('list');
        foreach($list as $i) {
            $this->redis->lPush('list', $i);
        }

        // SORT list → [ghi, def, abc]
        if (version_compare($this->version, "2.5.0") < 0) {
            $this->assertEquals(array_reverse($list), $this->redis->sort('list'));
            $this->assertEquals(array_reverse($list), $this->redis->sort('list', ['sort' => 'asc']));
        } else {
            // TODO rewrite, from 2.6.0 release notes:
            // SORT now will refuse to sort in numerical mode elements that can't be parsed
            // as numbers
        }

        // SORT list ALPHA → [abc, def, ghi]
        $this->assertEquals($list, $this->redis->sort('list', ['alpha' => TRUE]));
        $this->assertEquals($list, $this->redis->sort('list', ['sort' => 'asc', 'alpha' => TRUE]));
    }

    public function testSortDesc() {

    $this->setupSort();

    // sort by age and get IDs
    $byAgeDesc = ['4','2','1','3'];
    $this->assertEquals($byAgeDesc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'sort' => 'desc']));

    // sort by age and get names
    $byAgeDesc = ['Dave', 'Bob', 'Alice', 'Carol'];
    $this->assertEquals($byAgeDesc, $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'sort' => 'desc']));

    $this->assertEquals(array_slice($byAgeDesc, 0, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [0, 2], 'sort' => 'desc']));
    $this->assertEquals(array_slice($byAgeDesc, 1, 2), $this->redis->sort('person:id', ['by' => 'person:age_*', 'get' => 'person:name_*', 'limit' => [1, 2], 'sort' => 'desc']));

    // sort by salary and get ages
    $agesBySalaryDesc = ['41', '25', '27', '34'];
    $this->assertEquals($agesBySalaryDesc, $this->redis->sort('person:id', ['by' => 'person:salary_*', 'get' => 'person:age_*', 'sort' => 'desc']));

    // sort non-alpha doesn't change all-string lists
    $list = ['def', 'abc', 'ghi'];
    $this->redis->del('list');
    foreach($list as $i) {
        $this->redis->lPush('list', $i);
    }

    // SORT list → [ghi, abc, def]
    if (version_compare($this->version, "2.5.0") < 0) {
        $this->assertEquals(array_reverse($list), $this->redis->sort('list', ['sort' => 'desc']));
    } else {
        // TODO rewrite, from 2.6.0 release notes:
        // SORT now will refuse to sort in numerical mode elements that can't be parsed
        // as numbers
    }

    // SORT list ALPHA → [abc, def, ghi]
    $this->assertEquals(['ghi', 'def', 'abc'], $this->redis->sort('list', ['sort' => 'desc', 'alpha' => TRUE]));
    }

    // LINDEX
    public function testLindex() {

        $this->redis->del('list');

        $this->redis->lPush('list', 'val');
        $this->redis->lPush('list', 'val2');
        $this->redis->lPush('list', 'val3');

        $this->assertEquals('val3', $this->redis->lIndex('list', 0));
        $this->assertEquals('val2', $this->redis->lIndex('list', 1));
        $this->assertEquals('val', $this->redis->lIndex('list', 2));
        $this->assertEquals('val', $this->redis->lIndex('list', -1));
        $this->assertEquals('val2', $this->redis->lIndex('list', -2));
        $this->assertEquals('val3', $this->redis->lIndex('list', -3));
        $this->assertEquals(FALSE, $this->redis->lIndex('list', -4));

        $this->redis->rPush('list', 'val4');
        $this->assertEquals('val4', $this->redis->lIndex('list', 3));
        $this->assertEquals('val4', $this->redis->lIndex('list', -1));
    }

    // lRem testing
    public function testlrem() {
        $this->redis->del('list');
        $this->redis->lPush('list', 'a');
        $this->redis->lPush('list', 'b');
        $this->redis->lPush('list', 'c');
        $this->redis->lPush('list', 'c');
        $this->redis->lPush('list', 'b');
        $this->redis->lPush('list', 'c');

        // ['c', 'b', 'c', 'c', 'b', 'a']
        $return = $this->redis->lrem('list', 'b', 2);
        // ['c', 'c', 'c', 'a']
        $this->assertEquals(2, $return);
        $this->assertEquals('c', $this->redis->lIndex('list', 0));
        $this->assertEquals('c', $this->redis->lIndex('list', 1));
        $this->assertEquals('c', $this->redis->lIndex('list', 2));
        $this->assertEquals('a', $this->redis->lIndex('list', 3));

        $this->redis->del('list');
        $this->redis->lPush('list', 'a');
        $this->redis->lPush('list', 'b');
        $this->redis->lPush('list', 'c');
        $this->redis->lPush('list', 'c');
        $this->redis->lPush('list', 'b');
        $this->redis->lPush('list', 'c');

        // ['c', 'b', 'c', 'c', 'b', 'a']
        $this->redis->lrem('list', 'c', -2);
        // ['c', 'b', 'b', 'a']
        $this->assertEquals(2, $return);
        $this->assertEquals('c', $this->redis->lIndex('list', 0));
        $this->assertEquals('b', $this->redis->lIndex('list', 1));
        $this->assertEquals('b', $this->redis->lIndex('list', 2));
        $this->assertEquals('a', $this->redis->lIndex('list', 3));

        // remove each element
        $this->assertEquals(1, $this->redis->lrem('list', 'a', 0));
        $this->assertEquals(0, $this->redis->lrem('list', 'x', 0));
        $this->assertEquals(2, $this->redis->lrem('list', 'b', 0));
        $this->assertEquals(1, $this->redis->lrem('list', 'c', 0));
        $this->assertEquals(FALSE, $this->redis->get('list'));

        $this->redis->set('list', 'actually not a list');
        $this->assertEquals(FALSE, $this->redis->lrem('list', 'x'));
    }

    public function testsAdd() {
        $this->redis->del('set');

        $this->assertEquals(1, $this->redis->sAdd('set', 'val'));
        $this->assertEquals(0, $this->redis->sAdd('set', 'val'));

        $this->assertTrue($this->redis->sismember('set', 'val'));
        $this->assertFalse($this->redis->sismember('set', 'val2'));

        $this->assertEquals(1, $this->redis->sAdd('set', 'val2'));

        $this->assertTrue($this->redis->sismember('set', 'val2'));
    }

    public function testscard() {
        $this->redis->del('set');
        $this->assertEquals(1, $this->redis->sAdd('set', 'val'));
        $this->assertEquals(1, $this->redis->scard('set'));
        $this->assertEquals(1, $this->redis->sAdd('set', 'val2'));
        $this->assertEquals(2, $this->redis->scard('set'));
    }

    public function testsrem() {
        $this->redis->del('set');
        $this->redis->sAdd('set', 'val');
        $this->redis->sAdd('set', 'val2');
        $this->redis->srem('set', 'val');
        $this->assertEquals(1, $this->redis->scard('set'));
        $this->redis->srem('set', 'val2');
        $this->assertEquals(0, $this->redis->scard('set'));
    }

    public function testsMove() {
        $this->redis->del('{set}0');
        $this->redis->del('{set}1');

        $this->redis->sAdd('{set}0', 'val');
        $this->redis->sAdd('{set}0', 'val2');

        $this->assertTrue($this->redis->sMove('{set}0', '{set}1', 'val'));
        $this->assertFalse($this->redis->sMove('{set}0', '{set}1', 'val'));
        $this->assertFalse($this->redis->sMove('{set}0', '{set}1', 'val-what'));

        $this->assertEquals(1, $this->redis->scard('{set}0'));
        $this->assertEquals(1, $this->redis->scard('{set}1'));

        $this->assertEquals(['val2'], $this->redis->smembers('{set}0'));
        $this->assertEquals(['val'], $this->redis->smembers('{set}1'));
    }

    public function testsPop() {
        $this->redis->del('set0');
        $this->assertTrue($this->redis->sPop('set0') === FALSE);

        $this->redis->sAdd('set0', 'val');
        $this->redis->sAdd('set0', 'val2');

        $v0 = $this->redis->sPop('set0');
        $this->assertTrue(1 === $this->redis->scard('set0'));
        $this->assertTrue($v0 === 'val' || $v0 === 'val2');
        $v1 = $this->redis->sPop('set0');
        $this->assertTrue(0 === $this->redis->scard('set0'));
        $this->assertTrue(($v0 === 'val' && $v1 === 'val2') || ($v1 === 'val' && $v0 === 'val2'));

        $this->assertTrue($this->redis->sPop('set0') === FALSE);
    }

    public function testsPopWithCount() {
        if (!$this->minVersionCheck("3.2")) {
            return $this->markTestSkipped();
        }

        $set = 'set0';
        $prefix = 'member';
        $count = 5;

        /* Add a few members */
        $this->redis->del($set);
        for ($i = 0; $i < $count; $i++) {
            $this->redis->sadd($set, $prefix.$i);
        }

        /* Pop them all */
        $ret = $this->redis->sPop($set, $i);

        /* Make sure we got an arary and the count is right */
        if ($this->assertTrue(is_array($ret)) && $this->assertTrue(count($ret) == $count)) {
            /* Probably overkill but validate the actual returned members */
            for ($i = 0; $i < $count; $i++) {
                $this->assertTrue(in_array($prefix.$i, $ret));
            }
        }
    }

    public function testsRandMember() {
        $this->redis->del('set0');
        $this->assertTrue($this->redis->sRandMember('set0') === FALSE);

        $this->redis->sAdd('set0', 'val');
        $this->redis->sAdd('set0', 'val2');

        $got = [];
        while(true) {
            $v = $this->redis->sRandMember('set0');
            $this->assertTrue(2 === $this->redis->scard('set0')); // no change.
            $this->assertTrue($v === 'val' || $v === 'val2');

            $got[$v] = $v;
            if(count($got) == 2) {
                break;
            }
        }

        //
        // With and without count, while serializing
        //

        $this->redis->del('set0');
        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
        for($i=0;$i<5;$i++) {
            $member = "member:$i";
            $this->redis->sAdd('set0', $member);
            $mems[] = $member;
        }

        $member = $this->redis->srandmember('set0');
        $this->assertTrue(in_array($member, $mems));

        $rmembers = $this->redis->srandmember('set0', $i);
        foreach($rmembers as $reply_mem) {
            $this->assertTrue(in_array($reply_mem, $mems));
        }

        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
    }

    public function testSRandMemberWithCount() {
        // Make sure the set is nuked
        $this->redis->del('set0');

        // Run with a count (positive and negative) on an empty set
        $ret_pos = $this->redis->sRandMember('set0', 10);
        $ret_neg = $this->redis->sRandMember('set0', -10);

        // Should both be empty arrays
        $this->assertTrue(is_array($ret_pos) && empty($ret_pos));
        $this->assertTrue(is_array($ret_neg) && empty($ret_neg));

        // Add a few items to the set
        for($i=0;$i<100;$i++) {
            $this->redis->sadd('set0', "member$i");
        }

        // Get less than the size of the list
        $ret_slice = $this->redis->srandmember('set0', 20);

        // Should be an array with 20 items
        $this->assertTrue(is_array($ret_slice) && count($ret_slice) == 20);

        // Ask for more items than are in the list (but with a positive count)
        $ret_slice = $this->redis->srandmember('set0', 200);

        // Should be an array, should be however big the set is, exactly
        $this->assertTrue(is_array($ret_slice) && count($ret_slice) == $i);

        // Now ask for too many items but negative
        $ret_slice = $this->redis->srandmember('set0', -200);

        // Should be an array, should have exactly the # of items we asked for (will be dups)
        $this->assertTrue(is_array($ret_slice) && count($ret_slice) == 200);

        //
        // Test in a pipeline
        //

        if ($this->havePipeline()) {
            $pipe = $this->redis->pipeline();

            $pipe->srandmember('set0', 20);
            $pipe->srandmember('set0', 200);
            $pipe->srandmember('set0', -200);

            $ret = $this->redis->exec();

            $this->assertTrue(is_array($ret[0]) && count($ret[0]) == 20);
            $this->assertTrue(is_array($ret[1]) && count($ret[1]) == $i);
            $this->assertTrue(is_array($ret[2]) && count($ret[2]) == 200);

            // Kill the set
            $this->redis->del('set0');
        }
    }

    public function testsismember()
    {
        $this->redis->del('set');

        $this->redis->sAdd('set', 'val');

        $this->assertTrue($this->redis->sismember('set', 'val'));
        $this->assertFalse($this->redis->sismember('set', 'val2'));
    }

    public function testsmembers()
    {
        $this->redis->del('set');

        $this->redis->sAdd('set', 'val');
        $this->redis->sAdd('set', 'val2');
        $this->redis->sAdd('set', 'val3');

        $array = ['val', 'val2', 'val3'];

        $smembers = $this->redis->smembers('set');
        sort($smembers);
        $this->assertEquals($array, $smembers);

        $sMembers = $this->redis->sMembers('set');
        sort($sMembers);
        $this->assertEquals($array, $sMembers); // test alias
    }

    public function testsMisMember()
    {
        // Only available since 6.2.0
        if (version_compare($this->version, '6.2.0') < 0) {
            $this->markTestSkipped();
            return;
        }

        $this->redis->del('set');

        $this->redis->sAdd('set', 'val');
        $this->redis->sAdd('set', 'val2');
        $this->redis->sAdd('set', 'val3');

        $misMembers = $this->redis->sMisMember('set', 'val', 'notamember', 'val3');
        $this->assertEquals([1, 0, 1], $misMembers);

        $misMembers = $this->redis->sMisMember('wrongkey', 'val', 'val2', 'val3');
        $this->assertEquals([0, 0, 0], $misMembers);
    }

    public function testlSet() {

        $this->redis->del('list');
        $this->redis->lPush('list', 'val');
        $this->redis->lPush('list', 'val2');
    $this->redis->lPush('list', 'val3');

    $this->assertEquals($this->redis->lIndex('list', 0), 'val3');
    $this->assertEquals($this->redis->lIndex('list', 1), 'val2');
    $this->assertEquals($this->redis->lIndex('list', 2), 'val');

    $this->assertEquals(TRUE, $this->redis->lSet('list', 1, 'valx'));

    $this->assertEquals($this->redis->lIndex('list', 0), 'val3');
    $this->assertEquals($this->redis->lIndex('list', 1), 'valx');
    $this->assertEquals($this->redis->lIndex('list', 2), 'val');

    }

    public function testsInter() {
        $this->redis->del('{set}odd');    // set of odd numbers
        $this->redis->del('{set}prime');  // set of prime numbers
        $this->redis->del('{set}square'); // set of squares
        $this->redis->del('{set}seq');    // set of numbers of the form n^2 - 1

        $x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach($x as $i) {
            $this->redis->sAdd('{set}odd', $i);
        }

        $y = [1,2,3,5,7,11,13,17,19,23];
        foreach($y as $i) {
            $this->redis->sAdd('{set}prime', $i);
        }

        $z = [1,4,9,16,25];
        foreach($z as $i) {
            $this->redis->sAdd('{set}square', $i);
        }

        $t = [2,5,10,17,26];
        foreach($t as $i) {
            $this->redis->sAdd('{set}seq', $i);
        }

        $xy = $this->redis->sInter('{set}odd', '{set}prime');   // odd prime numbers
        foreach($xy as $i) {
            $i = (int)$i;
            $this->assertTrue(in_array($i, array_intersect($x, $y)));
        }

        $xy = $this->redis->sInter(['{set}odd', '{set}prime']);    // odd prime numbers, as array.
        foreach($xy as $i) {
            $i = (int)$i;
            $this->assertTrue(in_array($i, array_intersect($x, $y)));
        }

        $yz = $this->redis->sInter('{set}prime', '{set}square');   // set of prime squares
        foreach($yz as $i) {
            $i = (int)$i;
            $this->assertTrue(in_array($i, array_intersect($y, $z)));
        }

        $yz = $this->redis->sInter(['{set}prime', '{set}square']);    // set of odd squares, as array
        foreach($yz as $i) {
        $i = (int)$i;
            $this->assertTrue(in_array($i, array_intersect($y, $z)));
        }

        $zt = $this->redis->sInter('{set}square', '{set}seq');   // prime squares
        $this->assertTrue($zt === []);
        $zt = $this->redis->sInter(['{set}square', '{set}seq']);    // prime squares, as array
        $this->assertTrue($zt === []);

        $xyz = $this->redis->sInter('{set}odd', '{set}prime', '{set}square');// odd prime squares
        $this->assertTrue($xyz === ['1']);

        $xyz = $this->redis->sInter(['{set}odd', '{set}prime', '{set}square']);// odd prime squares, with an array as a parameter
        $this->assertTrue($xyz === ['1']);

        $nil = $this->redis->sInter([]);
        $this->assertTrue($nil === FALSE);
    }

    public function testsInterStore() {
        $this->redis->del('{set}x');  // set of odd numbers
        $this->redis->del('{set}y');  // set of prime numbers
        $this->redis->del('{set}z');  // set of squares
        $this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        $x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach($x as $i) {
            $this->redis->sAdd('{set}x', $i);
        }

        $y = [1,2,3,5,7,11,13,17,19,23];
        foreach($y as $i) {
            $this->redis->sAdd('{set}y', $i);
        }

        $z = [1,4,9,16,25];
        foreach($z as $i) {
            $this->redis->sAdd('{set}z', $i);
        }

        $t = [2,5,10,17,26];
        foreach($t as $i) {
            $this->redis->sAdd('{set}t', $i);
        }

        /* Regression test for passing a single array */
        $this->assertEquals($this->redis->sInterStore(['{set}k', '{set}x', '{set}y']), count(array_intersect($x,$y)));

        $count = $this->redis->sInterStore('{set}k', '{set}x', '{set}y');  // odd prime numbers
        $this->assertEquals($count, $this->redis->scard('{set}k'));
        foreach(array_intersect($x, $y) as $i) {
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $count = $this->redis->sInterStore('{set}k', '{set}y', '{set}z');  // set of odd squares
        $this->assertEquals($count, $this->redis->scard('{set}k'));
        foreach(array_intersect($y, $z) as $i) {
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $count = $this->redis->sInterStore('{set}k', '{set}z', '{set}t');  // squares of the form n^2 + 1
        $this->assertEquals($count, 0);
        $this->assertEquals($count, $this->redis->scard('{set}k'));

        $this->redis->del('{set}z');
        $xyz = $this->redis->sInterStore('{set}k', '{set}x', '{set}y', '{set}z'); // only z missing, expect 0.
        $this->assertTrue($xyz === 0);

        $this->redis->del('{set}y');
        $xyz = $this->redis->sInterStore('{set}k', '{set}x', '{set}y', '{set}z'); // y and z missing, expect 0.
        $this->assertTrue($xyz === 0);

        $this->redis->del('{set}x');
        $xyz = $this->redis->sInterStore('{set}k', '{set}x', '{set}y', '{set}z'); // x y and z ALL missing, expect 0.
        $this->assertTrue($xyz === 0);
    }

    public function testsUnion() {
        $this->redis->del('{set}x');  // set of odd numbers
        $this->redis->del('{set}y');  // set of prime numbers
        $this->redis->del('{set}z');  // set of squares
        $this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        $x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach($x as $i) {
            $this->redis->sAdd('{set}x', $i);
        }

        $y = [1,2,3,5,7,11,13,17,19,23];
        foreach($y as $i) {
            $this->redis->sAdd('{set}y', $i);
        }

        $z = [1,4,9,16,25];
        foreach($z as $i) {
            $this->redis->sAdd('{set}z', $i);
        }

        $t = [2,5,10,17,26];
        foreach($t as $i) {
            $this->redis->sAdd('{set}t', $i);
        }

        $xy = $this->redis->sUnion('{set}x', '{set}y');   // x U y
        foreach($xy as $i) {
        $i = (int)$i;
            $this->assertTrue(in_array($i, array_merge($x, $y)));
        }

        $yz = $this->redis->sUnion('{set}y', '{set}z');   // y U Z
        foreach($yz as $i) {
        $i = (int)$i;
            $this->assertTrue(in_array($i, array_merge($y, $z)));
        }

        $zt = $this->redis->sUnion('{set}z', '{set}t');   // z U t
        foreach($zt as $i) {
        $i = (int)$i;
            $this->assertTrue(in_array($i, array_merge($z, $t)));
        }

        $xyz = $this->redis->sUnion('{set}x', '{set}y', '{set}z'); // x U y U z
        foreach($xyz as $i) {
        $i = (int)$i;
            $this->assertTrue(in_array($i, array_merge($x, $y, $z)));
        }
    }

    public function testsUnionStore() {
        $this->redis->del('{set}x');  // set of odd numbers
        $this->redis->del('{set}y');  // set of prime numbers
        $this->redis->del('{set}z');  // set of squares
        $this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        $x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach($x as $i) {
            $this->redis->sAdd('{set}x', $i);
        }

        $y = [1,2,3,5,7,11,13,17,19,23];
        foreach($y as $i) {
            $this->redis->sAdd('{set}y', $i);
        }

        $z = [1,4,9,16,25];
        foreach($z as $i) {
            $this->redis->sAdd('{set}z', $i);
        }

        $t = [2,5,10,17,26];
        foreach($t as $i) {
            $this->redis->sAdd('{set}t', $i);
        }

        $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y');  // x U y
        $xy = array_unique(array_merge($x, $y));
        $this->assertEquals($count, count($xy));
        foreach($xy as $i) {
        $i = (int)$i;
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $count = $this->redis->sUnionStore('{set}k', '{set}y', '{set}z');  // y U z
        $yz = array_unique(array_merge($y, $z));
        $this->assertEquals($count, count($yz));
        foreach($yz as $i) {
        $i = (int)$i;
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $count = $this->redis->sUnionStore('{set}k', '{set}z', '{set}t');  // z U t
        $zt = array_unique(array_merge($z, $t));
        $this->assertEquals($count, count($zt));
        foreach($zt as $i) {
        $i = (int)$i;
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z
        $xyz = array_unique(array_merge($x, $y, $z));
        $this->assertEquals($count, count($xyz));
        foreach($xyz as $i) {
        $i = (int)$i;
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $this->redis->del('{set}x');  // x missing now
        $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z
        $this->assertTrue($count === count(array_unique(array_merge($y, $z))));

        $this->redis->del('{set}y');  // x and y missing
        $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z
        $this->assertTrue($count === count(array_unique($z)));

        $this->redis->del('{set}z');  // x, y, and z ALL missing
        $count = $this->redis->sUnionStore('{set}k', '{set}x', '{set}y', '{set}z'); // x U y U z
        $this->assertTrue($count === 0);
    }

    public function testsDiff() {
        $this->redis->del('{set}x');  // set of odd numbers
        $this->redis->del('{set}y');  // set of prime numbers
        $this->redis->del('{set}z');  // set of squares
        $this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        $x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach($x as $i) {
            $this->redis->sAdd('{set}x', $i);
        }

        $y = [1,2,3,5,7,11,13,17,19,23];
        foreach($y as $i) {
            $this->redis->sAdd('{set}y', $i);
        }

        $z = [1,4,9,16,25];
        foreach($z as $i) {
            $this->redis->sAdd('{set}z', $i);
        }

        $t = [2,5,10,17,26];
        foreach($t as $i) {
            $this->redis->sAdd('{set}t', $i);
        }

        $xy = $this->redis->sDiff('{set}x', '{set}y');    // x U y
        foreach($xy as $i) {
        $i = (int)$i;
            $this->assertTrue(in_array($i, array_diff($x, $y)));
        }

        $yz = $this->redis->sDiff('{set}y', '{set}z');    // y U Z
        foreach($yz as $i) {
        $i = (int)$i;
            $this->assertTrue(in_array($i, array_diff($y, $z)));
        }

        $zt = $this->redis->sDiff('{set}z', '{set}t');    // z U t
        foreach($zt as $i) {
        $i = (int)$i;
            $this->assertTrue(in_array($i, array_diff($z, $t)));
        }

        $xyz = $this->redis->sDiff('{set}x', '{set}y', '{set}z'); // x U y U z
        foreach($xyz as $i) {
        $i = (int)$i;
            $this->assertTrue(in_array($i, array_diff($x, $y, $z)));
        }
    }

    public function testsDiffStore() {
        $this->redis->del('{set}x');  // set of odd numbers
        $this->redis->del('{set}y');  // set of prime numbers
        $this->redis->del('{set}z');  // set of squares
        $this->redis->del('{set}t');  // set of numbers of the form n^2 - 1

        $x = [1,3,5,7,9,11,13,15,17,19,21,23,25];
        foreach($x as $i) {
            $this->redis->sAdd('{set}x', $i);
        }

        $y = [1,2,3,5,7,11,13,17,19,23];
        foreach($y as $i) {
            $this->redis->sAdd('{set}y', $i);
        }

        $z = [1,4,9,16,25];
        foreach($z as $i) {
            $this->redis->sAdd('{set}z', $i);
        }

        $t = [2,5,10,17,26];
        foreach($t as $i) {
            $this->redis->sAdd('{set}t', $i);
        }

        $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y');   // x - y
        $xy = array_unique(array_diff($x, $y));
        $this->assertEquals($count, count($xy));
        foreach($xy as $i) {
            $i = (int)$i;
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $count = $this->redis->sDiffStore('{set}k', '{set}y', '{set}z');   // y - z
        $yz = array_unique(array_diff($y, $z));
        $this->assertEquals($count, count($yz));
        foreach($yz as $i) {
        $i = (int)$i;
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $count = $this->redis->sDiffStore('{set}k', '{set}z', '{set}t');   // z - t
        $zt = array_unique(array_diff($z, $t));
        $this->assertEquals($count, count($zt));
        foreach($zt as $i) {
        $i = (int)$i;
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z');  // x - y - z
        $xyz = array_unique(array_diff($x, $y, $z));
        $this->assertEquals($count, count($xyz));
        foreach($xyz as $i) {
        $i = (int)$i;
            $this->assertTrue($this->redis->sismember('{set}k', $i));
        }

        $this->redis->del('{set}x');  // x missing now
        $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z');  // x - y - z
        $this->assertTrue($count === 0);

        $this->redis->del('{set}y');  // x and y missing
        $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z');  // x - y - z
        $this->assertTrue($count === 0);

        $this->redis->del('{set}z');  // x, y, and z ALL missing
        $count = $this->redis->sDiffStore('{set}k', '{set}x', '{set}y', '{set}z');  // x - y - z
        $this->assertTrue($count === 0);
    }

    public function testlrange() {
        $this->redis->del('list');
        $this->redis->lPush('list', 'val');
        $this->redis->lPush('list', 'val2');
        $this->redis->lPush('list', 'val3');

        // pos :   0     1     2
        // pos :  -3    -2    -1
        // list: [val3, val2, val]

        $this->assertEquals($this->redis->lrange('list', 0, 0), ['val3']);
        $this->assertEquals($this->redis->lrange('list', 0, 1), ['val3', 'val2']);
        $this->assertEquals($this->redis->lrange('list', 0, 2), ['val3', 'val2', 'val']);
        $this->assertEquals($this->redis->lrange('list', 0, 3), ['val3', 'val2', 'val']);

        $this->assertEquals($this->redis->lrange('list', 0, -1), ['val3', 'val2', 'val']);
        $this->assertEquals($this->redis->lrange('list', 0, -2), ['val3', 'val2']);
        $this->assertEquals($this->redis->lrange('list', -2, -1), ['val2', 'val']);

        $this->redis->del('list');
        $this->assertEquals($this->redis->lrange('list', 0, -1), []);
    }

    public function testdbSize() {
        $this->assertTrue($this->redis->flushDB());
        $this->redis->set('x', 'y');
        $this->assertTrue($this->redis->dbSize() === 1);
    }

    public function testttl() {
        $this->redis->set('x', 'y');
        $this->redis->expire('x', 5);
        $ttl = $this->redis->ttl('x');
        $this->assertTrue($ttl > 0 && $ttl <= 5);

        // A key with no TTL
        $this->redis->del('x'); $this->redis->set('x', 'bar');
        $this->assertEquals($this->redis->ttl('x'), -1);

        // A key that doesn't exist (> 2.8 will return -2)
        if(version_compare($this->version, "2.8.0") >= 0) {
            $this->redis->del('x');
            $this->assertEquals($this->redis->ttl('x'), -2);
        }
    }

    public function testPersist() {
        $this->redis->set('x', 'y');
        $this->redis->expire('x', 100);
        $this->assertTrue(TRUE === $this->redis->persist('x'));     // true if there is a timeout
        $this->assertTrue(-1 === $this->redis->ttl('x'));       // -1: timeout has been removed.
        $this->assertTrue(FALSE === $this->redis->persist('x'));    // false if there is no timeout
        $this->redis->del('x');
        $this->assertTrue(FALSE === $this->redis->persist('x'));    // false if the key doesn’t exist.
    }

    public function testClient() {
        /* CLIENT SETNAME */
        $this->assertTrue($this->redis->client('setname', 'phpredis_unit_tests'));

        /* CLIENT LIST */
        $arr_clients = $this->redis->client('list');
        $this->assertTrue(is_array($arr_clients));

        // Figure out which ip:port is us!
        $str_addr = NULL;
        foreach($arr_clients as $arr_client) {
            if($arr_client['name'] == 'phpredis_unit_tests') {
                $str_addr = $arr_client['addr'];
            }
        }

        // We should have found our connection
        $this->assertFalse(empty($str_addr));

        /* CLIENT GETNAME */
        $this->assertTrue($this->redis->client('getname'), 'phpredis_unit_tests');

        /* CLIENT KILL -- phpredis will reconnect, so we can do this */
        $this->assertTrue($this->redis->client('kill', $str_addr));
    }

    public function testSlowlog() {
        // We don't really know what's going to be in the slowlog, but make sure
        // the command returns proper types when called in various ways
        $this->assertTrue(is_array($this->redis->slowlog('get')));
        $this->assertTrue(is_array($this->redis->slowlog('get', 10)));
        $this->assertTrue(is_int($this->redis->slowlog('len')));
        $this->assertTrue($this->redis->slowlog('reset'));
        $this->assertFalse($this->redis->slowlog('notvalid'));
    }

    public function testWait() {
        // Closest we can check based on redis commmit history
        if(version_compare($this->version, '2.9.11') < 0) {
            $this->markTestSkipped();
            return;
        }

        // We could have slaves here, so determine that
        $arr_slaves = $this->redis->info();
        $i_slaves   = $arr_slaves['connected_slaves'];

        // Send a couple commands
        $this->redis->set('wait-foo', 'over9000');
        $this->redis->set('wait-bar', 'revo9000');

        // Make sure we get the right replication count
        $this->assertEquals($this->redis->wait($i_slaves, 100), $i_slaves);

        // Pass more slaves than are connected
        $this->redis->set('wait-foo','over9000');
        $this->redis->set('wait-bar','revo9000');
        $this->assertTrue($this->redis->wait($i_slaves+1, 100) < $i_slaves+1);

        // Make sure when we pass with bad arguments we just get back false
        $this->assertFalse($this->redis->wait(-1, -1));
        $this->assertFalse($this->redis->wait(-1, 20));
    }

    public function testInfo() {
        foreach ([false, true] as $boo_multi) {
            if ($boo_multi) {
                $this->redis->multi();
                $this->redis->info();
                $info = $this->redis->exec();
                $info = $info[0];
            } else {
                $info = $this->redis->info();
            }

            $keys = [
                "redis_version",
                "arch_bits",
                "uptime_in_seconds",
                "uptime_in_days",
                "connected_clients",
                "connected_slaves",
                "used_memory",
                "total_connections_received",
                "total_commands_processed",
                "role"
            ];
            if (version_compare($this->version, "2.5.0") < 0) {
                array_push($keys,
                    "changes_since_last_save",
                    "bgsave_in_progress",
                    "last_save_time"
                );
            } else {
                array_push($keys,
                    "rdb_changes_since_last_save",
                    "rdb_bgsave_in_progress",
                    "rdb_last_save_time"
                );
            }

            foreach($keys as $k) {
                $this->assertTrue(in_array($k, array_keys($info)));
            }
        }
    }

    public function testInfoCommandStats() {

    // INFO COMMANDSTATS is new in 2.6.0
    if (version_compare($this->version, "2.5.0") < 0) {
        $this->markTestSkipped();
    }

    $info = $this->redis->info("COMMANDSTATS");

    $this->assertTrue(is_array($info));
    if (is_array($info)) {
        foreach($info as $k => $value) {
            $this->assertTrue(strpos($k, 'cmdstat_') !== false);
        }
    }
    }

    public function testSelect() {
        $this->assertFalse($this->redis->select(-1));
        $this->assertTrue($this->redis->select(0));
    }

    public function testSwapDB() {
        if (version_compare($this->version, "4.0.0") < 0) {
            $this->markTestSkipped();
        }

        $this->assertTrue($this->redis->swapdb(0, 1));
        $this->assertTrue($this->redis->swapdb(0, 1));
    }

    public function testMset() {
    $this->redis->del('x', 'y', 'z');    // remove x y z
    $this->assertTrue($this->redis->mset(['x' => 'a', 'y' => 'b', 'z' => 'c']));   // set x y z

    $this->assertEquals($this->redis->mget(['x', 'y', 'z']), ['a', 'b', 'c']);    // check x y z

    $this->redis->del('x');  // delete just x
    $this->assertTrue($this->redis->mset(['x' => 'a', 'y' => 'b', 'z' => 'c']));   // set x y z
    $this->assertEquals($this->redis->mget(['x', 'y', 'z']), ['a', 'b', 'c']);    // check x y z

    $this->assertFalse($this->redis->mset([])); // set ø → FALSE


    /*
     * Integer keys
     */

    // No prefix
    $set_array = [-1 => 'neg1', -2 => 'neg2', -3 => 'neg3', 1 => 'one', 2 => 'two', '3' => 'three'];
    $this->redis->del(array_keys($set_array));
    $this->assertTrue($this->redis->mset($set_array));
    $this->assertEquals($this->redis->mget(array_keys($set_array)), array_values($set_array));
    $this->redis->del(array_keys($set_array));

    // With a prefix
    $this->redis->setOption(Redis::OPT_PREFIX, 'pfx:');
    $this->redis->del(array_keys($set_array));
    $this->assertTrue($this->redis->mset($set_array));
    $this->assertEquals($this->redis->mget(array_keys($set_array)), array_values($set_array));
    $this->redis->del(array_keys($set_array));
    $this->redis->setOption(Redis::OPT_PREFIX, '');
    }

    public function testMsetNX() {
        $this->redis->del('x', 'y', 'z');    // remove x y z
        $this->assertTrue(TRUE === $this->redis->msetnx(['x' => 'a', 'y' => 'b', 'z' => 'c']));    // set x y z

        $this->assertEquals($this->redis->mget(['x', 'y', 'z']), ['a', 'b', 'c']);    // check x y z

        $this->redis->del('x');  // delete just x
        $this->assertTrue(FALSE === $this->redis->msetnx(['x' => 'A', 'y' => 'B', 'z' => 'C']));   // set x y z
        $this->assertEquals($this->redis->mget(['x', 'y', 'z']), [FALSE, 'b', 'c']);  // check x y z

        $this->assertFalse($this->redis->msetnx([])); // set ø → FALSE
    }

    public function testRpopLpush() {
        // standard case.
        $this->redis->del('{list}x', '{list}y');
        $this->redis->lpush('{list}x', 'abc');
        $this->redis->lpush('{list}x', 'def');    // x = [def, abc]

        $this->redis->lpush('{list}y', '123');
        $this->redis->lpush('{list}y', '456');    // y = [456, 123]

        $this->assertEquals($this->redis->rpoplpush('{list}x', '{list}y'), 'abc');  // we RPOP x, yielding abc.
        $this->assertEquals($this->redis->lrange('{list}x', 0, -1), ['def']); // only def remains in x.
        $this->assertEquals($this->redis->lrange('{list}y', 0, -1), ['abc', '456', '123']);   // abc has been lpushed to y.

        // with an empty source, expecting no change.
        $this->redis->del('{list}x', '{list}y');
        $this->assertTrue(FALSE === $this->redis->rpoplpush('{list}x', '{list}y'));
        $this->assertTrue([] === $this->redis->lrange('{list}x', 0, -1));
        $this->assertTrue([] === $this->redis->lrange('{list}y', 0, -1));
    }

    public function testBRpopLpush() {
        // standard case.
        $this->redis->del('{list}x', '{list}y');
        $this->redis->lpush('{list}x', 'abc');
        $this->redis->lpush('{list}x', 'def');    // x = [def, abc]

        $this->redis->lpush('{list}y', '123');
        $this->redis->lpush('{list}y', '456');    // y = [456, 123]

        $this->assertEquals($this->redis->brpoplpush('{list}x', '{list}y', 1), 'abc');  // we RPOP x, yielding abc.
        $this->assertEquals($this->redis->lrange('{list}x', 0, -1), ['def']); // only def remains in x.
        $this->assertEquals($this->redis->lrange('{list}y', 0, -1), ['abc', '456', '123']);   // abc has been lpushed to y.

        // with an empty source, expecting no change.
        $this->redis->del('{list}x', '{list}y');
        $this->assertTrue(FALSE === $this->redis->brpoplpush('{list}x', '{list}y', 1));
        $this->assertTrue([] === $this->redis->lrange('{list}x', 0, -1));
        $this->assertTrue([] === $this->redis->lrange('{list}y', 0, -1));
    }

    public function testZAddFirstArg() {

        $this->redis->del('key');

        $zsetName = 100; // not a string!
        $this->assertTrue(1 === $this->redis->zAdd($zsetName, 0, 'val0'));
        $this->assertTrue(1 === $this->redis->zAdd($zsetName, 1, 'val1'));

        $this->assertTrue(['val0', 'val1'] === $this->redis->zRange($zsetName, 0, -1));
    }

    public function testZX() {
        $this->redis->del('key');

        $this->assertTrue([] === $this->redis->zRange('key', 0, -1));
        $this->assertTrue([] === $this->redis->zRange('key', 0, -1, true));

        $this->assertTrue(1 === $this->redis->zAdd('key', 0, 'val0'));
        $this->assertTrue(1 === $this->redis->zAdd('key', 2, 'val2'));
        $this->assertTrue(2 === $this->redis->zAdd('key', 4, 'val4', 5, 'val5')); // multiple parameters
        if (version_compare($this->version, "3.0.2") < 0) {
            $this->assertTrue(1 === $this->redis->zAdd('key', 1, 'val1'));
            $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3'));
        } else {
            $this->assertTrue(1 === $this->redis->zAdd('key', [], 1, 'val1')); // empty options
            $this->assertTrue(1 === $this->redis->zAdd('key', ['nx'], 3, 'val3')); // nx option
            $this->assertTrue(0 === $this->redis->zAdd('key', ['xx'], 3, 'val3')); // xx option
        }

        $this->assertTrue(['val0', 'val1', 'val2', 'val3', 'val4', 'val5'] === $this->redis->zRange('key', 0, -1));

        // withscores
        $ret = $this->redis->zRange('key', 0, -1, true);
        $this->assertTrue(count($ret) == 6);
        $this->assertTrue($ret['val0'] == 0);
        $this->assertTrue($ret['val1'] == 1);
        $this->assertTrue($ret['val2'] == 2);
        $this->assertTrue($ret['val3'] == 3);
        $this->assertTrue($ret['val4'] == 4);
        $this->assertTrue($ret['val5'] == 5);

        $this->assertTrue(0 === $this->redis->zRem('key', 'valX'));
        $this->assertTrue(1 === $this->redis->zRem('key', 'val3'));
        $this->assertTrue(1 === $this->redis->zRem('key', 'val4'));
        $this->assertTrue(1 === $this->redis->zRem('key', 'val5'));

        $this->assertTrue(['val0', 'val1', 'val2'] === $this->redis->zRange('key', 0, -1));

        // zGetReverseRange

        $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'val3'));
        $this->assertTrue(1 === $this->redis->zAdd('key', 3, 'aal3'));

        $zero_to_three = $this->redis->zRangeByScore('key', 0, 3);
        $this->assertTrue(['val0', 'val1', 'val2', 'aal3', 'val3'] === $zero_to_three || ['val0', 'val1', 'val2', 'val3', 'aal3'] === $zero_to_three);

        $three_to_zero = $this->redis->zRevRangeByScore('key', 3, 0);
        $this->assertTrue(array_reverse(['val0', 'val1', 'val2', 'aal3', 'val3']) === $three_to_zero || array_reverse(['val0', 'val1', 'val2', 'val3', 'aal3']) === $three_to_zero);

        $this->assertTrue(5 === $this->redis->zCount('key', 0, 3));

        // withscores
        $this->redis->zRem('key', 'aal3');
        $zero_to_three = $this->redis->zRangeByScore('key', 0, 3, ['withscores' => TRUE]);
        $this->assertTrue(['val0' => 0, 'val1' => 1, 'val2' => 2, 'val3' => 3] == $zero_to_three);
        $this->assertTrue(4 === $this->redis->zCount('key', 0, 3));

        // limit
        $this->assertTrue(['val0'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 1]]));
        $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [0, 2]]));
        $this->assertTrue(['val1', 'val2'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [1, 2]]));
        $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => [0, 100]]));

        // limits as references
        $limit = [0, 100];
        foreach ($limit as &$val) {}
        $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => $limit]));

        $this->assertTrue(['val3'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 1]]));
        $this->assertTrue(['val3', 'val2'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [0, 2]]));
        $this->assertTrue(['val2', 'val1'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [1, 2]]));
        $this->assertTrue(['val1', 'val0'] === $this->redis->zRevRangeByScore('key', 1, 0, ['limit' => [0, 100]]));

        $this->assertTrue(4 === $this->redis->zCard('key'));
        $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1'));
        $this->assertFalse($this->redis->zScore('key', 'val'));
        $this->assertFalse($this->redis->zScore(3, 2));

        // with () and +inf, -inf
        $this->redis->del('zset');
        $this->redis->zAdd('zset', 1, 'foo');
        $this->redis->zAdd('zset', 2, 'bar');
        $this->redis->zAdd('zset', 3, 'biz');
        $this->redis->zAdd('zset', 4, 'foz');
        $this->assertTrue(['foo' => 1, 'bar' => 2, 'biz' => 3, 'foz' => 4] == $this->redis->zRangeByScore('zset', '-inf', '+inf', ['withscores' => TRUE]));
        $this->assertTrue(['foo' => 1, 'bar' => 2] == $this->redis->zRangeByScore('zset', 1, 2, ['withscores' => TRUE]));
        $this->assertTrue(['bar' => 2] == $this->redis->zRangeByScore('zset', '(1', 2, ['withscores' => TRUE]));
        $this->assertTrue([] == $this->redis->zRangeByScore('zset', '(1', '(2', ['withscores' => TRUE]));

        $this->assertTrue(4 == $this->redis->zCount('zset', '-inf', '+inf'));
        $this->assertTrue(2 == $this->redis->zCount('zset', 1, 2));
        $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2));
        $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2'));


        // zincrby
        $this->redis->del('key');
        $this->assertTrue(1.0 === $this->redis->zIncrBy('key', 1, 'val1'));
        $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1'));
        $this->assertTrue(2.5 === $this->redis->zIncrBy('key', 1.5, 'val1'));
        $this->assertTrue(2.5 === $this->redis->zScore('key', 'val1'));

        // zUnionStore
        $this->redis->del('{zset}1');
        $this->redis->del('{zset}2');
        $this->redis->del('{zset}3');
        $this->redis->del('{zset}U');

        $this->redis->zAdd('{zset}1', 0, 'val0');
        $this->redis->zAdd('{zset}1', 1, 'val1');

        $this->redis->zAdd('{zset}2', 2, 'val2');
        $this->redis->zAdd('{zset}2', 3, 'val3');

        $this->redis->zAdd('{zset}3', 4, 'val4');
        $this->redis->zAdd('{zset}3', 5, 'val5');

        $this->assertTrue(4 === $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}3']));
        $this->assertTrue(['val0', 'val1', 'val4', 'val5'] === $this->redis->zRange('{zset}U', 0, -1));

        // Union on non existing keys
        $this->redis->del('{zset}U');
        $this->assertTrue(0 === $this->redis->zUnionStore('{zset}U', ['{zset}X', '{zset}Y']));
        $this->assertTrue([] === $this->redis->zRange('{zset}U', 0, -1));

        // !Exist U Exist → copy of existing zset.
        $this->redis->del('{zset}U', 'X');
        $this->assertTrue(2 === $this->redis->zUnionStore('{zset}U', ['{zset}1', '{zset}X']));

        // test weighted zUnion
        $this->redis->del('{zset}Z');
        $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [1, 1]));
        $this->assertTrue(['val0', 'val1', 'val2', 'val3'] === $this->redis->zRange('{zset}Z', 0, -1));

        $this->redis->zRemRangeByScore('{zset}Z', 0, 10);
        $this->assertTrue(4 === $this->redis->zUnionStore('{zset}Z', ['{zset}1', '{zset}2'], [5, 1]));
        $this->assertTrue(['val0', 'val2', 'val3', 'val1'] === $this->redis->zRange('{zset}Z', 0, -1));

        $this->redis->del('{zset}1');
        $this->redis->del('{zset}2');
        $this->redis->del('{zset}3');

        //test zUnion with weights and aggegration function
        $this->redis->zadd('{zset}1', 1, 'duplicate');
        $this->redis->zadd('{zset}2', 2, 'duplicate');
        $this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], [1,1], 'MIN');
        $this->assertTrue($this->redis->zScore('{zset}U', 'duplicate')===1.0);
        $this->redis->del('{zset}U');

        //now test zUnion *without* weights but with aggregrate function
        $this->redis->zUnionStore('{zset}U', ['{zset}1','{zset}2'], null, 'MIN');
        $this->assertTrue($this->redis->zScore('{zset}U', 'duplicate')===1.0);
        $this->redis->del('{zset}U', '{zset}1', '{zset}2');

        // test integer and float weights (GitHub issue #109).
        $this->redis->del('{zset}1', '{zset}2', '{zset}3');

        $this->redis->zadd('{zset}1', 1, 'one');
        $this->redis->zadd('{zset}1', 2, 'two');
        $this->redis->zadd('{zset}2', 1, 'one');
        $this->redis->zadd('{zset}2', 2, 'two');
        $this->redis->zadd('{zset}2', 3, 'three');

        $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1', '{zset}2'], [2, 3.0]) === 3);

        $this->redis->del('{zset}1');
        $this->redis->del('{zset}2');
        $this->redis->del('{zset}3');

        // Test 'inf', '-inf', and '+inf' weights (GitHub issue #336)
        $this->redis->zadd('{zset}1', 1, 'one', 2, 'two', 3, 'three');
        $this->redis->zadd('{zset}2', 3, 'three', 4, 'four', 5, 'five');

        // Make sure phpredis handles these weights
        $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, 'inf'])  === 5);
        $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '-inf']) === 5);
        $this->assertTrue($this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1, '+inf']) === 5);

        // Now, confirm that they're being sent, and that it works
        $arr_weights = ['inf','-inf','+inf'];

        foreach($arr_weights as $str_weight) {
            $r = $this->redis->zUnionStore('{zset}3', ['{zset}1','{zset}2'], [1,$str_weight]);
            $this->assertTrue($r===5);
            $r = $this->redis->zrangebyscore('{zset}3', '(-inf', '(inf',['withscores'=>true]);
            $this->assertTrue(count($r)===2);
            $this->assertTrue(isset($r['one']));
            $this->assertTrue(isset($r['two']));
        }

        $this->redis->del('{zset}1','{zset}2','{zset}3');

        $this->redis->zadd('{zset}1', 2000.1, 'one');
        $this->redis->zadd('{zset}1', 3000.1, 'two');
        $this->redis->zadd('{zset}1', 4000.1, 'three');

        $ret = $this->redis->zRange('{zset}1', 0, -1, TRUE);
        $this->assertTrue(count($ret) === 3);
        $retValues = array_keys($ret);

        $this->assertTrue(['one', 'two', 'three'] === $retValues);

        // + 0 converts from string to float OR integer
        $this->assertTrue(is_float($ret['one'] + 0));
        $this->assertTrue(is_float($ret['two'] + 0));
        $this->assertTrue(is_float($ret['three'] + 0));

        $this->redis->del('{zset}1');

        // ZREMRANGEBYRANK
        $this->redis->zAdd('{zset}1', 1, 'one');
        $this->redis->zAdd('{zset}1', 2, 'two');
        $this->redis->zAdd('{zset}1', 3, 'three');
        $this->assertTrue(2 === $this->redis->zremrangebyrank('{zset}1', 0, 1));
        $this->assertTrue(['three' => 3] == $this->redis->zRange('{zset}1', 0, -1, TRUE));

        $this->redis->del('{zset}1');

        // zInterStore

        $this->redis->zAdd('{zset}1', 0, 'val0');
        $this->redis->zAdd('{zset}1', 1, 'val1');
        $this->redis->zAdd('{zset}1', 3, 'val3');

        $this->redis->zAdd('{zset}2', 2, 'val1');
        $this->redis->zAdd('{zset}2', 3, 'val3');

        $this->redis->zAdd('{zset}3', 4, 'val3');
        $this->redis->zAdd('{zset}3', 5, 'val5');

        $this->redis->del('{zset}I');
        $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2']));
        $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1));

        // Union on non existing keys
        $this->assertTrue(0 === $this->redis->zInterStore('{zset}X', ['{zset}X', '{zset}Y']));
        $this->assertTrue([] === $this->redis->zRange('{zset}X', 0, -1));

        // !Exist U Exist
        $this->assertTrue(0 === $this->redis->zInterStore('{zset}Y', ['{zset}1', '{zset}X']));
        $this->assertTrue([] === $this->redis->zRange('keyY', 0, -1));


        // test weighted zInterStore
        $this->redis->del('{zset}1');
        $this->redis->del('{zset}2');
        $this->redis->del('{zset}3');

        $this->redis->zAdd('{zset}1', 0, 'val0');
        $this->redis->zAdd('{zset}1', 1, 'val1');
        $this->redis->zAdd('{zset}1', 3, 'val3');


        $this->redis->zAdd('{zset}2', 2, 'val1');
        $this->redis->zAdd('{zset}2', 1, 'val3');

        $this->redis->zAdd('{zset}3', 7, 'val1');
        $this->redis->zAdd('{zset}3', 3, 'val3');

        $this->redis->del('{zset}I');
        $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2'], [1, 1]));
        $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1));

        $this->redis->del('{zset}I');
        $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'min'));
        $this->assertTrue(['val1', 'val3'] === $this->redis->zRange('{zset}I', 0, -1));
        $this->redis->del('{zset}I');
        $this->assertTrue( 2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], [1, 5, 1], 'max'));
        $this->assertTrue(['val3', 'val1'] === $this->redis->zRange('{zset}I', 0, -1));

        $this->redis->del('{zset}I');
        $this->assertTrue(2 === $this->redis->zInterStore('{zset}I', ['{zset}1', '{zset}2', '{zset}3'], null, 'max'));
        $this->assertTrue($this->redis->zScore('{zset}I', 'val1') === floatval(7));

        // zrank, zrevrank
        $this->redis->del('z');
        $this->redis->zadd('z', 1, 'one');
        $this->redis->zadd('z', 2, 'two');
        $this->redis->zadd('z', 5, 'five');

        $this->assertTrue(0 === $this->redis->zRank('z', 'one'));
        $this->assertTrue(1 === $this->redis->zRank('z', 'two'));
        $this->assertTrue(2 === $this->redis->zRank('z', 'five'));

        $this->assertTrue(2 === $this->redis->zRevRank('z', 'one'));
        $this->assertTrue(1 === $this->redis->zRevRank('z', 'two'));
        $this->assertTrue(0 === $this->redis->zRevRank('z', 'five'));
    }

    public function testZRangeScoreArg() {
        $this->redis->del('{z}');

        $arr_mems = ['one' => 1.0, 'two' => 2.0, 'three' => 3.0];
        foreach ($arr_mems as $str_mem => $score) {
            $this->redis->zAdd('{z}', $score, $str_mem);
        }

        /* Verify we can pass true and ['withscores' => true] */
        $this->assertEquals($arr_mems, $this->redis->zRange('{z}', 0, -1, true));
        $this->assertEquals($arr_mems, $this->redis->zRange('{z}', 0, -1, ['withscores' => true]));
    }

    public function testZRangeByLex() {
        /* ZRANGEBYLEX available on versions >= 2.8.9 */
        if(version_compare($this->version, "2.8.9") < 0) {
            $this->MarkTestSkipped();
            return;
        }

        $this->redis->del('key');
        foreach(range('a', 'g') as $c) {
            $this->redis->zAdd('key', 0, $c);
        }

        $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c'), ['a', 'b', 'c']);
        $this->assertEquals($this->redis->zRangeByLex('key', '(e', '+'), ['f', 'g']);

        // with limit offset
        $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c', 1, 2), ['b', 'c'] );
        $this->assertEquals($this->redis->zRangeByLex('key', '-', '(c', 1, 2), ['b']);
    }

    public function testZLexCount() {
        if (version_compare($this->version, "2.8.9") < 0) {
            $this->MarkTestSkipped();
            return;
        }

        $this->redis->del('key');
        foreach (range('a', 'g') as $c) {
            $entries[] = $c;
            $this->redis->zAdd('key', 0, $c);
        }

        /* Special -/+ values */
        $this->assertEquals($this->redis->zLexCount('key', '-', '-'), 0);
        $this->assertEquals($this->redis->zLexCount('key', '-', '+'), count($entries));

        /* Verify invalid arguments return FALSE */
        $this->assertFalse(@$this->redis->zLexCount('key', '[a', 'bad'));
        $this->assertFalse(@$this->redis->zLexCount('key', 'bad', '[a'));

        /* Now iterate through */
        $start = $entries[0];
        for ($i = 1; $i < count($entries); $i++) {
            $end = $entries[$i];
            $this->assertEquals($this->redis->zLexCount('key', "[$start", "[$end"), $i + 1);
            $this->assertEquals($this->redis->zLexCount('key', "[$start", "($end"), $i);
            $this->assertEquals($this->redis->zLexCount('key', "($start", "($end"), $i - 1);
        }
    }

    public function testZRemRangeByLex() {
        if (version_compare($this->version, "2.8.9") < 0) {
            $this->MarkTestSkipped();
            return;
        }

        $this->redis->del('key');
        $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c');
        $this->assertEquals($this->redis->zRemRangeByLex('key', '-', '+'), 3);

        $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c');
        $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '[c'), 3);

        $this->redis->zAdd('key', 0, 'a', 0, 'b', 0, 'c');
        $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '(a'), 0);
        $this->assertEquals($this->redis->zRemRangeByLex('key', '(a', '(c'), 1);
        $this->assertEquals($this->redis->zRemRangeByLex('key', '[a', '[c'), 2);
    }

    public function testBZPop() {
        if (version_compare($this->version, "5.0.0") < 0) {
            $this->MarkTestSkipped();
            return;
        }

        $this->redis->del('{zs}1', '{zs}2');
        $this->redis->zAdd('{zs}1', 0, 'a', 1, 'b', 2, 'c');
        $this->redis->zAdd('{zs}2', 3, 'A', 4, 'B', 5, 'D');

        $this->assertEquals(Array('{zs}1', 'a', '0'), $this->redis->bzPopMin('{zs}1', '{zs}2', 0));
        $this->assertEquals(Array('{zs}1', 'c', '2'), $this->redis->bzPopMax(Array('{zs}1', '{zs}2'), 0));
        $this->assertEquals(Array('{zs}2', 'A', '3'), $this->redis->bzPopMin('{zs}2', '{zs}1', 0));

        /* Verify timeout is being sent */
        $this->redis->del('{zs}1', '{zs}2');
        $st = microtime(true) * 1000;
        $this->redis->bzPopMin('{zs}1', '{zs}2', 1);
        $et = microtime(true) * 1000;
        $this->assertTrue($et - $st > 100);
    }

    public function testZPop() {
        if (version_compare($this->version, "5.0.0") < 0) {
            $this->MarkTestSkipped();
            return;
        }

        // zPopMax and zPopMin without a COUNT argument
        $this->redis->del('key');
        $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e');
        $this->assertTrue(array('e' => 4.0) === $this->redis->zPopMax('key'));
        $this->assertTrue(array('a' => 0.0) === $this->redis->zPopMin('key'));

        // zPopMax with a COUNT argument
        $this->redis->del('key');
        $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e');
        $this->assertTrue(array('e' => 4.0, 'd' => 3.0, 'c' => 2.0) === $this->redis->zPopMax('key', 3));

        // zPopMin with a COUNT argument
        $this->redis->del('key');
        $this->redis->zAdd('key', 0, 'a', 1, 'b', 2, 'c', 3, 'd', 4, 'e');
        $this->assertTrue(array('a' => 0.0, 'b' => 1.0, 'c' => 2.0) === $this->redis->zPopMin('key', 3));
    }

    public function testHashes() {
        $this->redis->del('h', 'key');
        $this->assertTrue(0 === $this->redis->hLen('h'));
        $this->assertTrue(1 === $this->redis->hSet('h', 'a', 'a-value'));
        $this->assertTrue(1 === $this->redis->hLen('h'));
        $this->assertTrue(1 === $this->redis->hSet('h', 'b', 'b-value'));
        $this->assertTrue(2 === $this->redis->hLen('h'));

        $this->assertTrue('a-value' === $this->redis->hGet('h', 'a'));  // simple get
        $this->assertTrue('b-value' === $this->redis->hGet('h', 'b'));  // simple get

        $this->assertTrue(0 === $this->redis->hSet('h', 'a', 'another-value')); // replacement
        $this->assertTrue('another-value' === $this->redis->hGet('h', 'a'));    // get the new value

        $this->assertTrue('b-value' === $this->redis->hGet('h', 'b'));  // simple get
        $this->assertTrue(FALSE === $this->redis->hGet('h', 'c'));  // unknown hash member
        $this->assertTrue(FALSE === $this->redis->hGet('key', 'c'));    // unknownkey

        // hDel
        $this->assertTrue(1 === $this->redis->hDel('h', 'a')); // 1 on success
        $this->assertTrue(0 === $this->redis->hDel('h', 'a')); // 0 on failure

        $this->redis->del('h');
        $this->redis->hSet('h', 'x', 'a');
        $this->redis->hSet('h', 'y', 'b');
        $this->assertTrue(2 === $this->redis->hDel('h', 'x', 'y')); // variadic

        // hsetnx
        $this->redis->del('h');
        $this->assertTrue(TRUE === $this->redis->hSetNx('h', 'x', 'a'));
        $this->assertTrue(TRUE === $this->redis->hSetNx('h', 'y', 'b'));
        $this->assertTrue(FALSE === $this->redis->hSetNx('h', 'x', '?'));
        $this->assertTrue(FALSE === $this->redis->hSetNx('h', 'y', '?'));
        $this->assertTrue('a' === $this->redis->hGet('h', 'x'));
        $this->assertTrue('b' === $this->redis->hGet('h', 'y'));

        // keys
        $keys = $this->redis->hKeys('h');
        $this->assertTrue($keys === ['x', 'y'] || $keys === ['y', 'x']);

        // values
        $values = $this->redis->hVals('h');
        $this->assertTrue($values === ['a', 'b'] || $values === ['b', 'a']);

        // keys + values
        $all = $this->redis->hGetAll('h');
        $this->assertTrue($all === ['x' => 'a', 'y' => 'b'] || $all === ['y' => 'b', 'x' => 'a']);

        // hExists
        $this->assertTrue(TRUE === $this->redis->hExists('h', 'x'));
        $this->assertTrue(TRUE === $this->redis->hExists('h', 'y'));
        $this->assertTrue(FALSE === $this->redis->hExists('h', 'w'));
        $this->redis->del('h');
        $this->assertTrue(FALSE === $this->redis->hExists('h', 'x'));

        // hIncrBy
        $this->redis->del('h');
        $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', 2));
        $this->assertTrue(3 === $this->redis->hIncrBy('h', 'x', 1));
        $this->assertTrue(2 === $this->redis->hIncrBy('h', 'x', -1));
        $this->assertTrue("2" === $this->redis->hGet('h', 'x'));
        $this->assertTrue(PHP_INT_MAX === $this->redis->hIncrBy('h', 'x', PHP_INT_MAX-2));
        $this->assertTrue("".PHP_INT_MAX === $this->redis->hGet('h', 'x'));

        $this->redis->hSet('h', 'y', 'not-a-number');
        $this->assertTrue(FALSE === $this->redis->hIncrBy('h', 'y', 1));

        if (version_compare($this->version, "2.5.0") >= 0) {
            // hIncrByFloat
            $this->redis->del('h');
            $this->assertTrue(1.5 === $this->redis->hIncrByFloat('h','x', 1.5));
            $this->assertTrue(3.0 === $this->redis->hincrByFloat('h','x', 1.5));
            $this->assertTrue(1.5 === $this->redis->hincrByFloat('h','x', -1.5));
            $this->assertTrue(1000000000001.5 === $this->redis->hincrByFloat('h','x', 1000000000000));

            $this->redis->hset('h','y','not-a-number');
            $this->assertTrue(FALSE === $this->redis->hIncrByFloat('h', 'y', 1.5));
        }

        // hmset
        $this->redis->del('h');
        $this->assertTrue(TRUE === $this->redis->hMset('h', ['x' => 123, 'y' => 456, 'z' => 'abc']));
        $this->assertTrue('123' === $this->redis->hGet('h', 'x'));
        $this->assertTrue('456' === $this->redis->hGet('h', 'y'));
        $this->assertTrue('abc' === $this->redis->hGet('h', 'z'));
        $this->assertTrue(FALSE === $this->redis->hGet('h', 't'));

        // hmget
        $this->assertTrue(['x' => '123', 'y' => '456'] === $this->redis->hMget('h', ['x', 'y']));
        $this->assertTrue(['z' => 'abc'] === $this->redis->hMget('h', ['z']));
        $this->assertTrue(['x' => '123', 't' => FALSE, 'y' => '456'] === $this->redis->hMget('h', ['x', 't', 'y']));
        $this->assertFalse([123 => 'x'] === $this->redis->hMget('h', [123]));
        $this->assertTrue([123 => FALSE] === $this->redis->hMget('h', [123]));

        // Test with an array populated with things we can't use as keys
        $this->assertTrue($this->redis->hmget('h', [false,NULL,false]) === FALSE);

        // Test with some invalid keys mixed in (which should just be ignored)
        $this->assertTrue(['x'=>'123','y'=>'456','z'=>'abc'] === $this->redis->hMget('h',['x',null,'y','','z',false]));

        // hmget/hmset with numeric fields
        $this->redis->del('h');
        $this->assertTrue(TRUE === $this->redis->hMset('h', [123 => 'x', 'y' => 456]));
        $this->assertTrue('x' === $this->redis->hGet('h', 123));
        $this->assertTrue('x' === $this->redis->hGet('h', '123'));
        $this->assertTrue('456' === $this->redis->hGet('h', 'y'));
        $this->assertTrue([123 => 'x', 'y' => '456'] === $this->redis->hMget('h', ['123', 'y']));

        // references
        $keys = [123, 'y'];
        foreach ($keys as &$key) {}
        $this->assertTrue([123 => 'x', 'y' => '456'] === $this->redis->hMget('h', $keys));

        // check non-string types.
        $this->redis->del('h1');
        $this->assertTrue(TRUE === $this->redis->hMSet('h1', ['x' => 0, 'y' => [], 'z' => new stdclass(), 't' => NULL]));
        $h1 = $this->redis->hGetAll('h1');
        $this->assertTrue('0' === $h1['x']);
        $this->assertTrue('Array' === $h1['y']);
        $this->assertTrue('Object' === $h1['z']);
        $this->assertTrue('' === $h1['t']);

        // hstrlen
        if (version_compare($this->version, '3.2.0') >= 0) {
            $this->redis->del('h');
            $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // key doesn't exist
            $this->redis->hSet('h', 'foo', 'bar');
            $this->assertTrue(0 === $this->redis->hStrLen('h', 'x')); // field is not present in the hash
            $this->assertTrue(3 === $this->redis->hStrLen('h', 'foo'));
	}
    }

    public function testSetRange() {

        $this->redis->del('key');
        $this->redis->set('key', 'hello world');
        $this->redis->setRange('key', 6, 'redis');
        $this->assertTrue('hello redis' === $this->redis->get('key'));
        $this->redis->setRange('key', 6, 'you'); // don't cut off the end
        $this->assertTrue('hello youis' === $this->redis->get('key'));

        $this->redis->set('key', 'hello world');
        // $this->assertTrue(11 === $this->redis->setRange('key', -6, 'redis')); // works with negative offsets too! (disabled because not all versions support this)
        // $this->assertTrue('hello redis' === $this->redis->get('key'));

        // fill with zeros if needed
        $this->redis->del('key');
        $this->redis->setRange('key', 6, 'foo');
        $this->assertTrue("\x00\x00\x00\x00\x00\x00foo" === $this->redis->get('key'));
    }

    public function testObject() {
        /* Version 3.0.0 (represented as >= 2.9.0 in redis info)  and moving
         * forward uses "embstr" instead of "raw" for small string values */
        if (version_compare($this->version, "2.9.0") < 0) {
            $str_small_encoding = "raw";
        } else {
            $str_small_encoding = "embstr";
        }

        $this->redis->del('key');
        $this->assertTrue($this->redis->object('encoding', 'key') === FALSE);
        $this->assertTrue($this->redis->object('refcount', 'key') === FALSE);
        $this->assertTrue($this->redis->object('idletime', 'key') === FALSE);

        $this->redis->set('key', 'value');
        $this->assertTrue($this->redis->object('encoding', 'key') === $str_small_encoding);
        $this->assertTrue($this->redis->object('refcount', 'key') === 1);
        $this->assertTrue($this->redis->object('idletime', 'key') === 0);

        $this->redis->del('key');
        $this->redis->lpush('key', 'value');

        /* Newer versions of redis are going to encode lists as 'quicklists',
         * so 'quicklist' or 'ziplist' is valid here */
        $str_encoding = $this->redis->object('encoding', 'key');
        $this->assertTrue($str_encoding === "ziplist" || $str_encoding === 'quicklist');

        $this->assertTrue($this->redis->object('refcount', 'key') === 1);
        $this->assertTrue($this->redis->object('idletime', 'key') === 0);

        $this->redis->del('key');
        $this->redis->sadd('key', 'value');
        $this->assertTrue($this->redis->object('encoding', 'key') === "hashtable");
        $this->assertTrue($this->redis->object('refcount', 'key') === 1);
        $this->assertTrue($this->redis->object('idletime', 'key') === 0);

        $this->redis->del('key');
        $this->redis->sadd('key', 42);
        $this->redis->sadd('key', 1729);
        $this->assertTrue($this->redis->object('encoding', 'key') === "intset");
        $this->assertTrue($this->redis->object('refcount', 'key') === 1);
        $this->assertTrue($this->redis->object('idletime', 'key') === 0);

        $this->redis->del('key');
        $this->redis->lpush('key', str_repeat('A', pow(10,6))); // 1M elements, too big for a ziplist.

        $str_encoding = $this->redis->object('encoding', 'key');
        $this->assertTrue($str_encoding === "linkedlist" || $str_encoding == "quicklist");

        $this->assertTrue($this->redis->object('refcount', 'key') === 1);
        $this->assertTrue($this->redis->object('idletime', 'key') === 0);
    }

    public function testMultiExec() {
        $this->sequence(Redis::MULTI);
        $this->differentType(Redis::MULTI);

        // with prefix as well
        $this->redis->setOption(Redis::OPT_PREFIX, "test:");
        $this->sequence(Redis::MULTI);
        $this->differentType(Redis::MULTI);
        $this->redis->setOption(Redis::OPT_PREFIX, "");

        $this->redis->set('x', '42');

        $this->assertTrue(TRUE === $this->redis->watch('x'));
        $ret = $this->redis->multi()->get('x')->exec();

        // successful transaction
        $this->assertTrue($ret === ['42']);
    }

    public function testFailedTransactions() {
        $this->redis->set('x', 42);

        // failed transaction
        $this->redis->watch('x');

        $r = $this->newInstance(); // new instance, modifying `x'.
        $r->incr('x');

        $ret = $this->redis->multi()->get('x')->exec();
        $this->assertTrue($ret === FALSE); // failed because another client changed our watched key between WATCH and EXEC.

        // watch and unwatch
        $this->redis->watch('x');
        $r->incr('x'); // other instance
        $this->redis->unwatch(); // cancel transaction watch

        $ret = $this->redis->multi()->get('x')->exec();

        $this->assertTrue($ret === ['44']); // succeeded since we've cancel the WATCH command.
    }

    public function testPipeline() {
        if (!$this->havePipeline()) {
            $this->markTestSkipped();
        }

        $this->sequence(Redis::PIPELINE);
        $this->differentType(Redis::PIPELINE);

        // with prefix as well
        $this->redis->setOption(Redis::OPT_PREFIX, "test:");
        $this->sequence(Redis::PIPELINE);
        $this->differentType(Redis::PIPELINE);
        $this->redis->setOption(Redis::OPT_PREFIX, "");
    }

    public function testPipelineMultiExec()
    {
        if (!$this->havePipeline()) {
            $this->markTestSkipped();
        }

        $ret = $this->redis->pipeline()->multi()->exec()->exec();
        $this->assertTrue(is_array($ret));
        $this->assertEquals(1, count($ret)); // empty transaction

        $ret = $this->redis->pipeline()
            ->ping()
            ->multi()->set('x', 42)->incr('x')->exec()
            ->ping()
            ->multi()->get('x')->del('x')->exec()
            ->ping()
            ->exec();
        $this->assertTrue(is_array($ret));
        $this->assertEquals(5, count($ret)); // should be 5 atomic operations
    }

    /* Github issue #1211 (ignore redundant calls to pipeline or multi) */
    public function testDoublePipeNoOp() {
        /* Only the first pipeline should be honored */
        for ($i = 0; $i < 6; $i++) {
            $this->redis->pipeline();
        }

        /* Set and get in our pipeline */
        $this->redis->set('pipecount','over9000')->get('pipecount');

        $data = $this->redis->exec();
        $this->assertEquals([true,'over9000'], $data);

        /* Only the first MULTI should be honored */
        for ($i = 0; $i < 6; $i++) {
            $this->redis->multi();
        }

        /* Set and get in our MULTI block */
        $this->redis->set('multicount', 'over9000')->get('multicount');

        $data = $this->redis->exec();
        $this->assertEquals([true, 'over9000'], $data);
    }

    public function testDiscard()
    {
        foreach ([Redis::PIPELINE, Redis::MULTI] as $mode) {
            /* start transaction */
            $this->redis->multi($mode);

            /* Set and get in our transaction */
            $this->redis->set('pipecount','over9000')->get('pipecount');

            /* first call closes transaction and clears commands queue */
            $this->assertTrue($this->redis->discard());

            /* next call fails because mode is ATOMIC */
            $this->assertFalse($this->redis->discard());
        }
    }

    protected function sequence($mode) {
        $ret = $this->redis->multi($mode)
            ->set('x', 42)
            ->type('x')
            ->get('x')
            ->exec();

        $this->assertTrue(is_array($ret));
        $i = 0;
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] === Redis::REDIS_STRING);
        $this->assertTrue($ret[$i] === '42' || $ret[$i] === 42);

        $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER);
        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer
        $ret = $this->redis->multi($mode)
            ->del('{key}1')
            ->set('{key}1', 'value1')
            ->get('{key}1')
            ->getSet('{key}1', 'value2')
            ->get('{key}1')
            ->set('{key}2', 4)
            ->incr('{key}2')
            ->get('{key}2')
            ->decr('{key}2')
            ->get('{key}2')
            ->rename('{key}2', '{key}3')
            ->get('{key}3')
            ->renameNx('{key}3', '{key}1')
            ->rename('{key}3', '{key}2')
            ->incrby('{key}2', 5)
            ->get('{key}2')
            ->decrby('{key}2', 5)
            ->get('{key}2')
            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i++]));
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 'value1');
        $this->assertTrue($ret[$i++] == 'value1');
        $this->assertTrue($ret[$i++] == 'value2');
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 5);
        $this->assertTrue($ret[$i++] == 5);
        $this->assertTrue($ret[$i++] == 4);
        $this->assertTrue($ret[$i++] == 4);
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 4);
        $this->assertTrue($ret[$i++] == FALSE);
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 9);
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 4);
        $this->assertTrue(count($ret) == $i);

        $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);

        $ret = $this->redis->multi($mode)
            ->del('{key}1')
            ->del('{key}2')
            ->set('{key}1', 'val1')
            ->setnx('{key}1', 'valX')
            ->setnx('{key}2', 'valX')
            ->exists('{key}1')
            ->exists('{key}3')
            ->exec();

        $this->assertTrue(is_array($ret));
        $this->assertTrue($ret[0] == TRUE);
        $this->assertTrue($ret[1] == TRUE);
        $this->assertTrue($ret[2] == TRUE);
        $this->assertTrue($ret[3] == FALSE);
        $this->assertTrue($ret[4] == TRUE);
        $this->assertTrue($ret[5] == TRUE);
        $this->assertTrue($ret[6] == FALSE);

        // ttl, mget, mset, msetnx, expire, expireAt
        $this->redis->del('key');
        $ret = $this->redis->multi($mode)
            ->ttl('key')
            ->mget(['{key}1', '{key}2', '{key}3'])
            ->mset(['{key}3' => 'value3', '{key}4' => 'value4'])
            ->set('key', 'value')
            ->expire('key', 5)
            ->ttl('key')
            ->expireAt('key', '0000')
            ->exec();

        $this->assertTrue(is_array($ret));
        $i = 0;
        $ttl = $ret[$i++];
        $this->assertTrue($ttl === -1 || $ttl === -2);
        $this->assertTrue($ret[$i++] === ['val1', 'valX', FALSE]); // mget
        $this->assertTrue($ret[$i++] === TRUE); // mset
        $this->assertTrue($ret[$i++] === TRUE); // set
        $this->assertTrue($ret[$i++] === TRUE); // expire
        $this->assertTrue($ret[$i++] === 5);    // ttl
        $this->assertTrue($ret[$i++] === TRUE); // expireAt
        $this->assertTrue(count($ret) == $i);

        $ret = $this->redis->multi($mode)
            ->set('{list}lkey', 'x')
            ->set('{list}lDest', 'y')
            ->del('{list}lkey', '{list}lDest')
            ->rpush('{list}lkey', 'lvalue')
            ->lpush('{list}lkey', 'lvalue')
            ->lpush('{list}lkey', 'lvalue')
            ->lpush('{list}lkey', 'lvalue')
            ->lpush('{list}lkey', 'lvalue')
            ->lpush('{list}lkey', 'lvalue')
            ->rpoplpush('{list}lkey', '{list}lDest')
            ->lrange('{list}lDest', 0, -1)
            ->lpop('{list}lkey')
            ->llen('{list}lkey')
            ->lrem('{list}lkey', 'lvalue', 3)
            ->llen('{list}lkey')
            ->lIndex('{list}lkey', 0)
            ->lrange('{list}lkey', 0, -1)
            ->lSet('{list}lkey', 1, "newValue")    // check errors on key not exists
            ->lrange('{list}lkey', 0, -1)
            ->llen('{list}lkey')
            ->exec();

        $this->assertTrue(is_array($ret));
        $i = 0;
        $this->assertTrue($ret[$i++] === TRUE); // SET
        $this->assertTrue($ret[$i++] === TRUE); // SET
        $this->assertTrue($ret[$i++] === 2); // deleting 2 keys
        $this->assertTrue($ret[$i++] === 1); // rpush, now 1 element
        $this->assertTrue($ret[$i++] === 2); // lpush, now 2 elements
        $this->assertTrue($ret[$i++] === 3); // lpush, now 3 elements
        $this->assertTrue($ret[$i++] === 4); // lpush, now 4 elements
        $this->assertTrue($ret[$i++] === 5); // lpush, now 5 elements
        $this->assertTrue($ret[$i++] === 6); // lpush, now 6 elements
        $this->assertTrue($ret[$i++] === 'lvalue'); // rpoplpush returns the element: "lvalue"
        $this->assertTrue($ret[$i++] === ['lvalue']); // lDest contains only that one element.
        $this->assertTrue($ret[$i++] === 'lvalue'); // removing a second element from lkey, now 4 elements left ↓
        $this->assertTrue($ret[$i++] === 4); // 4 elements left, after 2 pops.
        $this->assertTrue($ret[$i++] === 3); // removing 3 elements, now 1 left.
        $this->assertTrue($ret[$i++] === 1); // 1 element left
        $this->assertTrue($ret[$i++] === "lvalue"); // this is the current head.
        $this->assertTrue($ret[$i++] === ["lvalue"]); // this is the current list.
        $this->assertTrue($ret[$i++] === FALSE); // updating a non-existent element fails.
        $this->assertTrue($ret[$i++] === ["lvalue"]); // this is the current list.
        $this->assertTrue($ret[$i++] === 1); // 1 element left
        $this->assertTrue(count($ret) == $i);


        $ret = $this->redis->multi($mode)
            ->del('{list}lkey', '{list}lDest')
            ->rpush('{list}lkey', 'lvalue')
            ->lpush('{list}lkey', 'lvalue')
            ->lpush('{list}lkey', 'lvalue')
            ->rpoplpush('{list}lkey', '{list}lDest')
            ->lrange('{list}lDest', 0, -1)
            ->lpop('{list}lkey')
            ->exec();
        $this->assertTrue(is_array($ret));
        $i = 0;
        $this->assertTrue($ret[$i++] <= 2); // deleted 0, 1, or 2 items
        $this->assertTrue($ret[$i++] === 1); // 1 element in the list
        $this->assertTrue($ret[$i++] === 2); // 2 elements in the list
        $this->assertTrue($ret[$i++] === 3); // 3 elements in the list
        $this->assertTrue($ret[$i++] === 'lvalue'); // rpoplpush returns the element: "lvalue"
        $this->assertTrue($ret[$i++] === ['lvalue']); // rpoplpush returns the element: "lvalue"
        $this->assertTrue($ret[$i++] === 'lvalue'); // pop returns the front element: "lvalue"
        $this->assertTrue(count($ret) == $i);


        $serializer = $this->redis->getOption(Redis::OPT_SERIALIZER);
        $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // testing incr, which doesn't work with the serializer
        $ret = $this->redis->multi($mode)
            ->del('{key}1')
            ->set('{key}1', 'value1')
            ->get('{key}1')
            ->getSet('{key}1', 'value2')
            ->get('{key}1')
            ->set('{key}2', 4)
            ->incr('{key}2')
            ->get('{key}2')
            ->decr('{key}2')
            ->get('{key}2')
            ->rename('{key}2', '{key}3')
            ->get('{key}3')
            ->renameNx('{key}3', '{key}1')
            ->rename('{key}3', '{key}2')
            ->incrby('{key}2', 5)
            ->get('{key}2')
            ->decrby('{key}2', 5)
            ->get('{key}2')
            ->set('{key}3', 'value3')
            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i]) && $ret[$i] <= 1); $i++;
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 'value1');
        $this->assertTrue($ret[$i++] == 'value1');
        $this->assertTrue($ret[$i++] == 'value2');
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 5);
        $this->assertTrue($ret[$i++] == 5);
        $this->assertTrue($ret[$i++] == 4);
        $this->assertTrue($ret[$i++] == 4);
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 4);
        $this->assertTrue($ret[$i++] == FALSE);
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 9);
        $this->assertTrue($ret[$i++] == TRUE);
        $this->assertTrue($ret[$i++] == 4);
        $this->assertTrue($ret[$i++]);
        $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);

        $ret = $this->redis->multi($mode)
            ->del('{key}1')
            ->del('{key}2')
            ->del('{key}3')
            ->set('{key}1', 'val1')
            ->setnx('{key}1', 'valX')
            ->setnx('{key}2', 'valX')
            ->exists('{key}1')
            ->exists('{key}3')
            ->exec();

        $this->assertTrue(is_array($ret));
        $this->assertTrue($ret[0] == TRUE);
        $this->assertTrue($ret[1] == TRUE);
        $this->assertTrue($ret[2] == TRUE);
        $this->assertTrue($ret[3] == TRUE);
        $this->assertTrue($ret[4] == FALSE);
        $this->assertTrue($ret[5] == TRUE);
        $this->assertTrue($ret[6] == TRUE);
        $this->assertTrue($ret[7] == FALSE);

        // ttl, mget, mset, msetnx, expire, expireAt
        $ret = $this->redis->multi($mode)
            ->ttl('key')
            ->mget(['{key}1', '{key}2', '{key}3'])
            ->mset(['{key}3' => 'value3', '{key}4' => 'value4'])
            ->set('key', 'value')
            ->expire('key', 5)
            ->ttl('key')
            ->expireAt('key', '0000')
            ->exec();
        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i++]));
        $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 3); // mget
        $i++;
        $this->assertTrue($ret[$i++] === TRUE); // mset always returns TRUE
        $this->assertTrue($ret[$i++] === TRUE); // set always returns TRUE
        $this->assertTrue($ret[$i++] === TRUE); // expire always returns TRUE
        $this->assertTrue($ret[$i++] === 5); // TTL was just set.
        $this->assertTrue($ret[$i++] === TRUE); // expireAt returns TRUE for an existing key
        $this->assertTrue(count($ret) === $i);

        // lists
        $ret = $this->redis->multi($mode)
            ->del('{l}key', '{l}Dest')
            ->rpush('{l}key', 'lvalue')
            ->lpush('{l}key', 'lvalue')
            ->lpush('{l}key', 'lvalue')
            ->lpush('{l}key', 'lvalue')
            ->lpush('{l}key', 'lvalue')
            ->lpush('{l}key', 'lvalue')
            ->rpoplpush('{l}key', '{l}Dest')
            ->lrange('{l}Dest', 0, -1)
            ->lpop('{l}key')
            ->llen('{l}key')
            ->lrem('{l}key', 'lvalue', 3)
            ->llen('{l}key')
            ->lIndex('{l}key', 0)
            ->lrange('{l}key', 0, -1)
            ->lSet('{l}key', 1, "newValue")    // check errors on missing key
            ->lrange('{l}key', 0, -1)
            ->llen('{l}key')
            ->exec();

        $this->assertTrue(is_array($ret));
        $i = 0;
        $this->assertTrue($ret[$i] >= 0 && $ret[$i] <= 2); // del
        $i++;
        $this->assertTrue($ret[$i++] === 1); // 1 value
        $this->assertTrue($ret[$i++] === 2); // 2 values
        $this->assertTrue($ret[$i++] === 3); // 3 values
        $this->assertTrue($ret[$i++] === 4); // 4 values
        $this->assertTrue($ret[$i++] === 5); // 5 values
        $this->assertTrue($ret[$i++] === 6); // 6 values
        $this->assertTrue($ret[$i++] === 'lvalue');
        $this->assertTrue($ret[$i++] === ['lvalue']); // 1 value only in lDest
        $this->assertTrue($ret[$i++] === 'lvalue'); // now 4 values left
        $this->assertTrue($ret[$i++] === 4);
        $this->assertTrue($ret[$i++] === 3); // removing 3 elements.
        $this->assertTrue($ret[$i++] === 1); // length is now 1
        $this->assertTrue($ret[$i++] === 'lvalue'); // this is the head
        $this->assertTrue($ret[$i++] === ['lvalue']); // 1 value only in lkey
        $this->assertTrue($ret[$i++] === FALSE); // can't set list[1] if we only have a single value in it.
        $this->assertTrue($ret[$i++] === ['lvalue']); // the previous error didn't touch anything.
        $this->assertTrue($ret[$i++] === 1); // the previous error didn't change the length
        $this->assertTrue(count($ret) === $i);


        // sets
        $ret = $this->redis->multi($mode)
            ->del('{s}key1', '{s}key2', '{s}keydest', '{s}keyUnion', '{s}DiffDest')
            ->sadd('{s}key1', 'sValue1')
            ->sadd('{s}key1', 'sValue2')
            ->sadd('{s}key1', 'sValue3')
            ->sadd('{s}key1', 'sValue4')

            ->sadd('{s}key2', 'sValue1')
            ->sadd('{s}key2', 'sValue2')

            ->scard('{s}key1')
            ->srem('{s}key1', 'sValue2')
            ->scard('{s}key1')
            ->sMove('{s}key1', '{s}key2', 'sValue4')
            ->scard('{s}key2')
            ->sismember('{s}key2', 'sValue4')
            ->sMembers('{s}key1')
            ->sMembers('{s}key2')
            ->sInter('{s}key1', '{s}key2')
            ->sInterStore('{s}keydest', '{s}key1', '{s}key2')
            ->sMembers('{s}keydest')
            ->sUnion('{s}key2', '{s}keydest')
            ->sUnionStore('{s}keyUnion', '{s}key2', '{s}keydest')
            ->sMembers('{s}keyUnion')
            ->sDiff('{s}key1', '{s}key2')
            ->sDiffStore('{s}DiffDest', '{s}key1', '{s}key2')
            ->sMembers('{s}DiffDest')
            ->sPop('{s}key2')
            ->scard('{s}key2')
            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 5); $i++; // deleted at most 5 values.
        $this->assertTrue($ret[$i++] === 1); // skey1 now has 1 element.
        $this->assertTrue($ret[$i++] === 1); // skey1 now has 2 elements.
        $this->assertTrue($ret[$i++] === 1); // skey1 now has 3 elements.
        $this->assertTrue($ret[$i++] === 1); // skey1 now has 4 elements.

        $this->assertTrue($ret[$i++] === 1); // skey2 now has 1 element.
        $this->assertTrue($ret[$i++] === 1); // skey2 now has 2 elements.

        $this->assertTrue($ret[$i++] === 4);
        $this->assertTrue($ret[$i++] === 1); // we did remove that value.
        $this->assertTrue($ret[$i++] === 3); // now 3 values only.
        $this->assertTrue($ret[$i++] === TRUE); // the move did succeed.
        $this->assertTrue($ret[$i++] === 3); // sKey2 now has 3 values.
        $this->assertTrue($ret[$i++] === TRUE); // sKey2 does contain sValue4.
        foreach(['sValue1', 'sValue3'] as $k) { // sKey1 contains sValue1 and sValue3.
            $this->assertTrue(in_array($k, $ret[$i]));
        }
        $this->assertTrue(count($ret[$i++]) === 2);
        foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // sKey2 contains sValue1, sValue2, and sValue4.
            $this->assertTrue(in_array($k, $ret[$i]));
        }
        $this->assertTrue(count($ret[$i++]) === 3);
        $this->assertTrue($ret[$i++] === ['sValue1']); // intersection
        $this->assertTrue($ret[$i++] === 1); // intersection + store → 1 value in the destination set.
        $this->assertTrue($ret[$i++] === ['sValue1']); // sinterstore destination contents

        foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeydest U sKey2) contains sValue1, sValue2, and sValue4.
            $this->assertTrue(in_array($k, $ret[$i]));
        }
        $this->assertTrue(count($ret[$i++]) === 3); // union size

        $this->assertTrue($ret[$i++] === 3); // unionstore size
        foreach(['sValue1', 'sValue2', 'sValue4'] as $k) { // (skeyUnion) contains sValue1, sValue2, and sValue4.
            $this->assertTrue(in_array($k, $ret[$i]));
        }
        $this->assertTrue(count($ret[$i++]) === 3); // skeyUnion size

        $this->assertTrue($ret[$i++] === ['sValue3']); // diff skey1, skey2 : only sValue3 is not shared.
        $this->assertTrue($ret[$i++] === 1); // sdiffstore size == 1
        $this->assertTrue($ret[$i++] === ['sValue3']); // contents of sDiffDest

        $this->assertTrue(in_array($ret[$i++], ['sValue1', 'sValue2', 'sValue4'])); // we removed an element from sKey2
        $this->assertTrue($ret[$i++] === 2); // sKey2 now has 2 elements only.

        $this->assertTrue(count($ret) === $i);

        // sorted sets
        $ret = $this->redis->multi($mode)
            ->del('{z}key1', '{z}key2', '{z}key5', '{z}Inter', '{z}Union')
            ->zadd('{z}key1', 1, 'zValue1')
            ->zadd('{z}key1', 5, 'zValue5')
            ->zadd('{z}key1', 2, 'zValue2')
            ->zRange('{z}key1', 0, -1)
            ->zRem('{z}key1', 'zValue2')
            ->zRange('{z}key1', 0, -1)
            ->zadd('{z}key1', 11, 'zValue11')
            ->zadd('{z}key1', 12, 'zValue12')
            ->zadd('{z}key1', 13, 'zValue13')
            ->zadd('{z}key1', 14, 'zValue14')
            ->zadd('{z}key1', 15, 'zValue15')
            ->zRemRangeByScore('{z}key1', 11, 13)
            ->zrange('{z}key1', 0, -1)
            ->zRevRange('{z}key1', 0, -1)
            ->zRangeByScore('{z}key1', 1, 6)
            ->zCard('{z}key1')
            ->zScore('{z}key1', 'zValue15')
            ->zadd('{z}key2', 5, 'zValue5')
            ->zadd('{z}key2', 2, 'zValue2')
            ->zInterStore('{z}Inter', ['{z}key1', '{z}key2'])
            ->zRange('{z}key1', 0, -1)
            ->zRange('{z}key2', 0, -1)
            ->zRange('{z}Inter', 0, -1)
            ->zUnionStore('{z}Union', ['{z}key1', '{z}key2'])
            ->zRange('{z}Union', 0, -1)
            ->zadd('{z}key5', 5, 'zValue5')
            ->zIncrBy('{z}key5', 3, 'zValue5') // fix this
            ->zScore('{z}key5', 'zValue5')
            ->zScore('{z}key5', 'unknown')
            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i]) && $ret[$i] >= 0 && $ret[$i] <= 5); $i++; // deleting at most 5 keys
        $this->assertTrue($ret[$i++] === 1);
        $this->assertTrue($ret[$i++] === 1);
        $this->assertTrue($ret[$i++] === 1);
        $this->assertTrue($ret[$i++] === ['zValue1', 'zValue2', 'zValue5']);
        $this->assertTrue($ret[$i++] === 1);
        $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5']);
        $this->assertTrue($ret[$i++] === 1); // adding zValue11
        $this->assertTrue($ret[$i++] === 1); // adding zValue12
        $this->assertTrue($ret[$i++] === 1); // adding zValue13
        $this->assertTrue($ret[$i++] === 1); // adding zValue14
        $this->assertTrue($ret[$i++] === 1); // adding zValue15
        $this->assertTrue($ret[$i++] === 3); // deleted zValue11, zValue12, zValue13
        $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5', 'zValue14', 'zValue15']);
        $this->assertTrue($ret[$i++] === ['zValue15', 'zValue14', 'zValue5', 'zValue1']);
        $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5']);
        $this->assertTrue($ret[$i++] === 4); // 4 elements
        $this->assertTrue($ret[$i++] === 15.0);
        $this->assertTrue($ret[$i++] === 1); // added value
        $this->assertTrue($ret[$i++] === 1); // added value
        $this->assertTrue($ret[$i++] === 1); // zinter only has 1 value
        $this->assertTrue($ret[$i++] === ['zValue1', 'zValue5', 'zValue14', 'zValue15']); // {z}key1 contents
        $this->assertTrue($ret[$i++] === ['zValue2', 'zValue5']); // {z}key2 contents
        $this->assertTrue($ret[$i++] === ['zValue5']); // {z}inter contents
        $this->assertTrue($ret[$i++] === 5); // {z}Union has 5 values (1,2,5,14,15)
        $this->assertTrue($ret[$i++] === ['zValue1', 'zValue2', 'zValue5', 'zValue14', 'zValue15']); // {z}Union contents
        $this->assertTrue($ret[$i++] === 1); // added value to {z}key5, with score 5
        $this->assertTrue($ret[$i++] === 8.0); // incremented score by 3 → it is now 8.
        $this->assertTrue($ret[$i++] === 8.0); // current score is 8.
        $this->assertTrue($ret[$i++] === FALSE); // score for unknown element.

        $this->assertTrue(count($ret) === $i);

        // hash
        $ret = $this->redis->multi($mode)
            ->del('hkey1')
            ->hset('hkey1', 'key1', 'value1')
            ->hset('hkey1', 'key2', 'value2')
            ->hset('hkey1', 'key3', 'value3')
            ->hmget('hkey1', ['key1', 'key2', 'key3'])
            ->hget('hkey1', 'key1')
            ->hlen('hkey1')
            ->hdel('hkey1', 'key2')
            ->hdel('hkey1', 'key2')
            ->hexists('hkey1', 'key2')
            ->hkeys('hkey1')
            ->hvals('hkey1')
            ->hgetall('hkey1')
            ->hset('hkey1', 'valn', 1)
            ->hset('hkey1', 'val-fail', 'non-string')
            ->hget('hkey1', 'val-fail')
            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue($ret[$i++] <= 1); // delete
        $this->assertTrue($ret[$i++] === 1); // added 1 element
        $this->assertTrue($ret[$i++] === 1); // added 1 element
        $this->assertTrue($ret[$i++] === 1); // added 1 element
        $this->assertTrue($ret[$i++] === ['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3']); // hmget, 3 elements
        $this->assertTrue($ret[$i++] === 'value1'); // hget
        $this->assertTrue($ret[$i++] === 3); // hlen
        $this->assertTrue($ret[$i++] === 1); // hdel succeeded
        $this->assertTrue($ret[$i++] === 0); // hdel failed
        $this->assertTrue($ret[$i++] === FALSE); // hexists didn't find the deleted key
        $this->assertTrue($ret[$i] === ['key1', 'key3'] || $ret[$i] === ['key3', 'key1']); $i++; // hkeys
        $this->assertTrue($ret[$i] === ['value1', 'value3'] || $ret[$i] === ['value3', 'value1']); $i++; // hvals
        $this->assertTrue($ret[$i] === ['key1' => 'value1', 'key3' => 'value3'] || $ret[$i] === ['key3' => 'value3', 'key1' => 'value1']); $i++; // hgetall
        $this->assertTrue($ret[$i++] === 1); // added 1 element
        $this->assertTrue($ret[$i++] === 1); // added the element, so 1.
        $this->assertTrue($ret[$i++] === 'non-string'); // hset succeeded
        $this->assertTrue(count($ret) === $i);

        $ret = $this->redis->multi($mode) // default to MULTI, not PIPELINE.
            ->del('test')
            ->set('test', 'xyz')
            ->get('test')
            ->exec();
        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue($ret[$i++] <= 1); // delete
        $this->assertTrue($ret[$i++] === TRUE); // added 1 element
        $this->assertTrue($ret[$i++] === 'xyz');
        $this->assertTrue(count($ret) === $i);

        // GitHub issue 78
        $this->redis->del('test');
        for($i = 1; $i <= 5; $i++)
            $this->redis->zadd('test', $i, (string)$i);

        $result = $this->redis->multi($mode)
            ->zscore('test', "1")
            ->zscore('test', "6")
            ->zscore('test', "8")
            ->zscore('test', "2")
            ->exec();

        $this->assertTrue($result === [1.0, FALSE, FALSE, 2.0]);
    }

    protected function differentType($mode) {

        // string
        $key = '{hash}string';
        $dkey = '{hash}' . __FUNCTION__;

        $ret = $this->redis->multi($mode)
            ->del($key)
            ->set($key, 'value')

            // lists I/F
            ->rPush($key, 'lvalue')
            ->lPush($key, 'lvalue')
            ->lLen($key)
            ->lPop($key)
            ->lrange($key, 0, -1)
            ->lTrim($key, 0, 1)
            ->lIndex($key, 0)
            ->lSet($key, 0, "newValue")
            ->lrem($key, 'lvalue', 1)
            ->lPop($key)
            ->rPop($key)
            ->rPoplPush($key, $dkey . 'lkey1')

            // sets I/F
            ->sAdd($key, 'sValue1')
            ->srem($key, 'sValue1')
            ->sPop($key)
            ->sMove($key, $dkey . 'skey1', 'sValue1')

            ->scard($key)
            ->sismember($key, 'sValue1')
            ->sInter($key, $dkey . 'skey2')

            ->sUnion($key, $dkey . 'skey4')
            ->sDiff($key, $dkey . 'skey7')
            ->sMembers($key)
            ->sRandMember($key)

            // sorted sets I/F
            ->zAdd($key, 1, 'zValue1')
            ->zRem($key, 'zValue1')
            ->zIncrBy($key, 1, 'zValue1')
            ->zRank($key, 'zValue1')
            ->zRevRank($key, 'zValue1')
            ->zRange($key, 0, -1)
            ->zRevRange($key, 0, -1)
            ->zRangeByScore($key, 1, 2)
            ->zCount($key, 0, -1)
            ->zCard($key)
            ->zScore($key, 'zValue1')
            ->zRemRangeByRank($key, 1, 2)
            ->zRemRangeByScore($key, 1, 2)

            // hash I/F
            ->hSet($key, 'key1', 'value1')
            ->hGet($key, 'key1')
            ->hMGet($key, ['key1'])
            ->hMSet($key, ['key1' => 'value1'])
            ->hIncrBy($key, 'key2', 1)
            ->hExists($key, 'key2')
            ->hDel($key, 'key2')
            ->hLen($key)
            ->hKeys($key)
            ->hVals($key)
            ->hGetAll($key)

            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i++])); // delete
        $this->assertTrue($ret[$i++] === TRUE); // set

        $this->assertTrue($ret[$i++] === FALSE); // rpush
        $this->assertTrue($ret[$i++] === FALSE); // lpush
        $this->assertTrue($ret[$i++] === FALSE); // llen
        $this->assertTrue($ret[$i++] === FALSE); // lpop
        $this->assertTrue($ret[$i++] === FALSE); // lrange
        $this->assertTrue($ret[$i++] === FALSE); // ltrim
        $this->assertTrue($ret[$i++] === FALSE); // lindex
        $this->assertTrue($ret[$i++] === FALSE); // lset
        $this->assertTrue($ret[$i++] === FALSE); // lremove
        $this->assertTrue($ret[$i++] === FALSE); // lpop
        $this->assertTrue($ret[$i++] === FALSE); // rpop
        $this->assertTrue($ret[$i++] === FALSE); // rpoplush

        $this->assertTrue($ret[$i++] === FALSE); // sadd
        $this->assertTrue($ret[$i++] === FALSE); // sremove
        $this->assertTrue($ret[$i++] === FALSE); // spop
        $this->assertTrue($ret[$i++] === FALSE); // smove
        $this->assertTrue($ret[$i++] === FALSE); // scard
        $this->assertTrue($ret[$i++] === FALSE); // sismember
        $this->assertTrue($ret[$i++] === FALSE); // sinter
        $this->assertTrue($ret[$i++] === FALSE); // sunion
        $this->assertTrue($ret[$i++] === FALSE); // sdiff
        $this->assertTrue($ret[$i++] === FALSE); // smembers
        $this->assertTrue($ret[$i++] === FALSE); // srandmember

        $this->assertTrue($ret[$i++] === FALSE); // zadd
        $this->assertTrue($ret[$i++] === FALSE); // zrem
        $this->assertTrue($ret[$i++] === FALSE); // zincrby
        $this->assertTrue($ret[$i++] === FALSE); // zrank
        $this->assertTrue($ret[$i++] === FALSE); // zrevrank
        $this->assertTrue($ret[$i++] === FALSE); // zrange
        $this->assertTrue($ret[$i++] === FALSE); // zreverserange
        $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore
        $this->assertTrue($ret[$i++] === FALSE); // zcount
        $this->assertTrue($ret[$i++] === FALSE); // zcard
        $this->assertTrue($ret[$i++] === FALSE); // zscore
        $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank
        $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore

        $this->assertTrue($ret[$i++] === FALSE); // hset
        $this->assertTrue($ret[$i++] === FALSE); // hget
        $this->assertTrue($ret[$i++] === FALSE); // hmget
        $this->assertTrue($ret[$i++] === FALSE); // hmset
        $this->assertTrue($ret[$i++] === FALSE); // hincrby
        $this->assertTrue($ret[$i++] === FALSE); // hexists
        $this->assertTrue($ret[$i++] === FALSE); // hdel
        $this->assertTrue($ret[$i++] === FALSE); // hlen
        $this->assertTrue($ret[$i++] === FALSE); // hkeys
        $this->assertTrue($ret[$i++] === FALSE); // hvals
        $this->assertTrue($ret[$i++] === FALSE); // hgetall

        $this->assertEquals($i, count($ret));

        // list
        $key = '{hash}list';
        $dkey = '{hash}' . __FUNCTION__;
        $ret = $this->redis->multi($mode)
            ->del($key)
            ->lpush($key, 'lvalue')

            // string I/F
            ->get($key)
            ->getset($key, 'value2')
            ->append($key, 'append')
            ->getRange($key, 0, 8)
            ->mget([$key])
            ->incr($key)
            ->incrBy($key, 1)
            ->decr($key)
            ->decrBy($key, 1)

            // sets I/F
            ->sAdd($key, 'sValue1')
            ->srem($key, 'sValue1')
            ->sPop($key)
            ->sMove($key, $dkey . 'skey1', 'sValue1')
            ->scard($key)
            ->sismember($key, 'sValue1')
            ->sInter($key, $dkey . 'skey2')
            ->sUnion($key, $dkey . 'skey4')
            ->sDiff($key, $dkey . 'skey7')
            ->sMembers($key)
            ->sRandMember($key)

            // sorted sets I/F
            ->zAdd($key, 1, 'zValue1')
            ->zRem($key, 'zValue1')
            ->zIncrBy($key, 1, 'zValue1')
            ->zRank($key, 'zValue1')
            ->zRevRank($key, 'zValue1')
            ->zRange($key, 0, -1)
            ->zRevRange($key, 0, -1)
            ->zRangeByScore($key, 1, 2)
            ->zCount($key, 0, -1)
            ->zCard($key)
            ->zScore($key, 'zValue1')
            ->zRemRangeByRank($key, 1, 2)
            ->zRemRangeByScore($key, 1, 2)

            // hash I/F
            ->hSet($key, 'key1', 'value1')
            ->hGet($key, 'key1')
            ->hMGet($key, ['key1'])
            ->hMSet($key, ['key1' => 'value1'])
            ->hIncrBy($key, 'key2', 1)
            ->hExists($key, 'key2')
            ->hDel($key, 'key2')
            ->hLen($key)
            ->hKeys($key)
            ->hVals($key)
            ->hGetAll($key)

            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i++])); // delete
        $this->assertTrue($ret[$i++] === 1); // lpush

        $this->assertTrue($ret[$i++] === FALSE); // get
        $this->assertTrue($ret[$i++] === FALSE); // getset
        $this->assertTrue($ret[$i++] === FALSE); // append
        $this->assertTrue($ret[$i++] === FALSE); // getRange
        $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget
        $i++;
        $this->assertTrue($ret[$i++] === FALSE); // incr
        $this->assertTrue($ret[$i++] === FALSE); // incrBy
        $this->assertTrue($ret[$i++] === FALSE); // decr
        $this->assertTrue($ret[$i++] === FALSE); // decrBy

        $this->assertTrue($ret[$i++] === FALSE); // sadd
        $this->assertTrue($ret[$i++] === FALSE); // sremove
        $this->assertTrue($ret[$i++] === FALSE); // spop
        $this->assertTrue($ret[$i++] === FALSE); // smove
        $this->assertTrue($ret[$i++] === FALSE); // scard
        $this->assertTrue($ret[$i++] === FALSE); // sismember
        $this->assertTrue($ret[$i++] === FALSE); // sinter
        $this->assertTrue($ret[$i++] === FALSE); // sunion
        $this->assertTrue($ret[$i++] === FALSE); // sdiff
        $this->assertTrue($ret[$i++] === FALSE); // smembers
        $this->assertTrue($ret[$i++] === FALSE); // srandmember

        $this->assertTrue($ret[$i++] === FALSE); // zadd
        $this->assertTrue($ret[$i++] === FALSE); // zrem
        $this->assertTrue($ret[$i++] === FALSE); // zincrby
        $this->assertTrue($ret[$i++] === FALSE); // zrank
        $this->assertTrue($ret[$i++] === FALSE); // zrevrank
        $this->assertTrue($ret[$i++] === FALSE); // zrange
        $this->assertTrue($ret[$i++] === FALSE); // zreverserange
        $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore
        $this->assertTrue($ret[$i++] === FALSE); // zcount
        $this->assertTrue($ret[$i++] === FALSE); // zcard
        $this->assertTrue($ret[$i++] === FALSE); // zscore
        $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank
        $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore

        $this->assertTrue($ret[$i++] === FALSE); // hset
        $this->assertTrue($ret[$i++] === FALSE); // hget
        $this->assertTrue($ret[$i++] === FALSE); // hmget
        $this->assertTrue($ret[$i++] === FALSE); // hmset
        $this->assertTrue($ret[$i++] === FALSE); // hincrby
        $this->assertTrue($ret[$i++] === FALSE); // hexists
        $this->assertTrue($ret[$i++] === FALSE); // hdel
        $this->assertTrue($ret[$i++] === FALSE); // hlen
        $this->assertTrue($ret[$i++] === FALSE); // hkeys
        $this->assertTrue($ret[$i++] === FALSE); // hvals
        $this->assertTrue($ret[$i++] === FALSE); // hgetall

        $this->assertEquals($i, count($ret));

        // set
        $key = '{hash}set';
        $dkey = '{hash}' . __FUNCTION__;
        $ret = $this->redis->multi($mode)
            ->del($key)
            ->sAdd($key, 'sValue')

            // string I/F
            ->get($key)
            ->getset($key, 'value2')
            ->append($key, 'append')
            ->getRange($key, 0, 8)
            ->mget([$key])
            ->incr($key)
            ->incrBy($key, 1)
            ->decr($key)
            ->decrBy($key, 1)

            // lists I/F
            ->rPush($key, 'lvalue')
            ->lPush($key, 'lvalue')
            ->lLen($key)
            ->lPop($key)
            ->lrange($key, 0, -1)
            ->lTrim($key, 0, 1)
            ->lIndex($key, 0)
            ->lSet($key, 0, "newValue")
            ->lrem($key, 'lvalue', 1)
            ->lPop($key)
            ->rPop($key)
            ->rPoplPush($key, $dkey . 'lkey1')

            // sorted sets I/F
            ->zAdd($key, 1, 'zValue1')
            ->zRem($key, 'zValue1')
            ->zIncrBy($key, 1, 'zValue1')
            ->zRank($key, 'zValue1')
            ->zRevRank($key, 'zValue1')
            ->zRange($key, 0, -1)
            ->zRevRange($key, 0, -1)
            ->zRangeByScore($key, 1, 2)
            ->zCount($key, 0, -1)
            ->zCard($key)
            ->zScore($key, 'zValue1')
            ->zRemRangeByRank($key, 1, 2)
            ->zRemRangeByScore($key, 1, 2)

            // hash I/F
            ->hSet($key, 'key1', 'value1')
            ->hGet($key, 'key1')
            ->hMGet($key, ['key1'])
            ->hMSet($key, ['key1' => 'value1'])
            ->hIncrBy($key, 'key2', 1)
            ->hExists($key, 'key2')
            ->hDel($key, 'key2')
            ->hLen($key)
            ->hKeys($key)
            ->hVals($key)
            ->hGetAll($key)

            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i++])); // delete
        $this->assertTrue($ret[$i++] === 1); // zadd

        $this->assertTrue($ret[$i++] === FALSE); // get
        $this->assertTrue($ret[$i++] === FALSE); // getset
        $this->assertTrue($ret[$i++] === FALSE); // append
        $this->assertTrue($ret[$i++] === FALSE); // getRange
        $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget
        $i++;
        $this->assertTrue($ret[$i++] === FALSE); // incr
        $this->assertTrue($ret[$i++] === FALSE); // incrBy
        $this->assertTrue($ret[$i++] === FALSE); // decr
        $this->assertTrue($ret[$i++] === FALSE); // decrBy

        $this->assertTrue($ret[$i++] === FALSE); // rpush
        $this->assertTrue($ret[$i++] === FALSE); // lpush
        $this->assertTrue($ret[$i++] === FALSE); // llen
        $this->assertTrue($ret[$i++] === FALSE); // lpop
        $this->assertTrue($ret[$i++] === FALSE); // lrange
        $this->assertTrue($ret[$i++] === FALSE); // ltrim
        $this->assertTrue($ret[$i++] === FALSE); // lindex
        $this->assertTrue($ret[$i++] === FALSE); // lset
        $this->assertTrue($ret[$i++] === FALSE); // lremove
        $this->assertTrue($ret[$i++] === FALSE); // lpop
        $this->assertTrue($ret[$i++] === FALSE); // rpop
        $this->assertTrue($ret[$i++] === FALSE); // rpoplush

        $this->assertTrue($ret[$i++] === FALSE); // zadd
        $this->assertTrue($ret[$i++] === FALSE); // zrem
        $this->assertTrue($ret[$i++] === FALSE); // zincrby
        $this->assertTrue($ret[$i++] === FALSE); // zrank
        $this->assertTrue($ret[$i++] === FALSE); // zrevrank
        $this->assertTrue($ret[$i++] === FALSE); // zrange
        $this->assertTrue($ret[$i++] === FALSE); // zreverserange
        $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore
        $this->assertTrue($ret[$i++] === FALSE); // zcount
        $this->assertTrue($ret[$i++] === FALSE); // zcard
        $this->assertTrue($ret[$i++] === FALSE); // zscore
        $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank
        $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore

        $this->assertTrue($ret[$i++] === FALSE); // hset
        $this->assertTrue($ret[$i++] === FALSE); // hget
        $this->assertTrue($ret[$i++] === FALSE); // hmget
        $this->assertTrue($ret[$i++] === FALSE); // hmset
        $this->assertTrue($ret[$i++] === FALSE); // hincrby
        $this->assertTrue($ret[$i++] === FALSE); // hexists
        $this->assertTrue($ret[$i++] === FALSE); // hdel
        $this->assertTrue($ret[$i++] === FALSE); // hlen
        $this->assertTrue($ret[$i++] === FALSE); // hkeys
        $this->assertTrue($ret[$i++] === FALSE); // hvals
        $this->assertTrue($ret[$i++] === FALSE); // hgetall

        $this->assertEquals($i, count($ret));

        // sorted set
        $key = '{hash}sortedset';
        $dkey = '{hash}' . __FUNCTION__;
        $ret = $this->redis->multi($mode)
            ->del($key)
            ->zAdd($key, 0, 'zValue')

            // string I/F
            ->get($key)
            ->getset($key, 'value2')
            ->append($key, 'append')
            ->getRange($key, 0, 8)
            ->mget([$key])
            ->incr($key)
            ->incrBy($key, 1)
            ->decr($key)
            ->decrBy($key, 1)

            // lists I/F
            ->rPush($key, 'lvalue')
            ->lPush($key, 'lvalue')
            ->lLen($key)
            ->lPop($key)
            ->lrange($key, 0, -1)
            ->lTrim($key, 0, 1)
            ->lIndex($key, 0)
            ->lSet($key, 0, "newValue")
            ->lrem($key, 'lvalue', 1)
            ->lPop($key)
            ->rPop($key)
            ->rPoplPush($key, $dkey . 'lkey1')

            // sets I/F
            ->sAdd($key, 'sValue1')
            ->srem($key, 'sValue1')
            ->sPop($key)
            ->sMove($key, $dkey . 'skey1', 'sValue1')
            ->scard($key)
            ->sismember($key, 'sValue1')
            ->sInter($key, $dkey . 'skey2')
            ->sUnion($key, $dkey . 'skey4')
            ->sDiff($key, $dkey . 'skey7')
            ->sMembers($key)
            ->sRandMember($key)

            // hash I/F
            ->hSet($key, 'key1', 'value1')
            ->hGet($key, 'key1')
            ->hMGet($key, ['key1'])
            ->hMSet($key, ['key1' => 'value1'])
            ->hIncrBy($key, 'key2', 1)
            ->hExists($key, 'key2')
            ->hDel($key, 'key2')
            ->hLen($key)
            ->hKeys($key)
            ->hVals($key)
            ->hGetAll($key)

            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i++])); // delete
        $this->assertTrue($ret[$i++] === 1); // zadd

        $this->assertTrue($ret[$i++] === FALSE); // get
        $this->assertTrue($ret[$i++] === FALSE); // getset
        $this->assertTrue($ret[$i++] === FALSE); // append
        $this->assertTrue($ret[$i++] === FALSE); // getRange
        $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget
        $i++;
        $this->assertTrue($ret[$i++] === FALSE); // incr
        $this->assertTrue($ret[$i++] === FALSE); // incrBy
        $this->assertTrue($ret[$i++] === FALSE); // decr
        $this->assertTrue($ret[$i++] === FALSE); // decrBy

        $this->assertTrue($ret[$i++] === FALSE); // rpush
        $this->assertTrue($ret[$i++] === FALSE); // lpush
        $this->assertTrue($ret[$i++] === FALSE); // llen
        $this->assertTrue($ret[$i++] === FALSE); // lpop
        $this->assertTrue($ret[$i++] === FALSE); // lrange
        $this->assertTrue($ret[$i++] === FALSE); // ltrim
        $this->assertTrue($ret[$i++] === FALSE); // lindex
        $this->assertTrue($ret[$i++] === FALSE); // lset
        $this->assertTrue($ret[$i++] === FALSE); // lremove
        $this->assertTrue($ret[$i++] === FALSE); // lpop
        $this->assertTrue($ret[$i++] === FALSE); // rpop
        $this->assertTrue($ret[$i++] === FALSE); // rpoplush

        $this->assertTrue($ret[$i++] === FALSE); // sadd
        $this->assertTrue($ret[$i++] === FALSE); // sremove
        $this->assertTrue($ret[$i++] === FALSE); // spop
        $this->assertTrue($ret[$i++] === FALSE); // smove
        $this->assertTrue($ret[$i++] === FALSE); // scard
        $this->assertTrue($ret[$i++] === FALSE); // sismember
        $this->assertTrue($ret[$i++] === FALSE); // sinter
        $this->assertTrue($ret[$i++] === FALSE); // sunion
        $this->assertTrue($ret[$i++] === FALSE); // sdiff
        $this->assertTrue($ret[$i++] === FALSE); // smembers
        $this->assertTrue($ret[$i++] === FALSE); // srandmember

        $this->assertTrue($ret[$i++] === FALSE); // hset
        $this->assertTrue($ret[$i++] === FALSE); // hget
        $this->assertTrue($ret[$i++] === FALSE); // hmget
        $this->assertTrue($ret[$i++] === FALSE); // hmset
        $this->assertTrue($ret[$i++] === FALSE); // hincrby
        $this->assertTrue($ret[$i++] === FALSE); // hexists
        $this->assertTrue($ret[$i++] === FALSE); // hdel
        $this->assertTrue($ret[$i++] === FALSE); // hlen
        $this->assertTrue($ret[$i++] === FALSE); // hkeys
        $this->assertTrue($ret[$i++] === FALSE); // hvals
        $this->assertTrue($ret[$i++] === FALSE); // hgetall

        $this->assertEquals($i, count($ret));

        // hash
        $key = '{hash}hash';
        $dkey = '{hash}' . __FUNCTION__;
        $ret = $this->redis->multi($mode)
            ->del($key)
            ->hset($key, 'key1', 'hValue')

            // string I/F
            ->get($key)
            ->getset($key, 'value2')
            ->append($key, 'append')
            ->getRange($key, 0, 8)
            ->mget([$key])
            ->incr($key)
            ->incrBy($key, 1)
            ->decr($key)
            ->decrBy($key, 1)

            // lists I/F
            ->rPush($key, 'lvalue')
            ->lPush($key, 'lvalue')
            ->lLen($key)
            ->lPop($key)
            ->lrange($key, 0, -1)
            ->lTrim($key, 0, 1)
            ->lIndex($key, 0)
            ->lSet($key, 0, "newValue")
            ->lrem($key, 'lvalue', 1)
            ->lPop($key)
            ->rPop($key)
            ->rPoplPush($key, $dkey . 'lkey1')

            // sets I/F
            ->sAdd($key, 'sValue1')
            ->srem($key, 'sValue1')
            ->sPop($key)
            ->sMove($key, $dkey . 'skey1', 'sValue1')
            ->scard($key)
            ->sismember($key, 'sValue1')
            ->sInter($key, $dkey . 'skey2')
            ->sUnion($key, $dkey . 'skey4')
            ->sDiff($key, $dkey . 'skey7')
            ->sMembers($key)
            ->sRandMember($key)

            // sorted sets I/F
            ->zAdd($key, 1, 'zValue1')
            ->zRem($key, 'zValue1')
            ->zIncrBy($key, 1, 'zValue1')
            ->zRank($key, 'zValue1')
            ->zRevRank($key, 'zValue1')
            ->zRange($key, 0, -1)
            ->zRevRange($key, 0, -1)
            ->zRangeByScore($key, 1, 2)
            ->zCount($key, 0, -1)
            ->zCard($key)
            ->zScore($key, 'zValue1')
            ->zRemRangeByRank($key, 1, 2)
            ->zRemRangeByScore($key, 1, 2)

            ->exec();

        $i = 0;
        $this->assertTrue(is_array($ret));
        $this->assertTrue(is_long($ret[$i++])); // delete
        $this->assertTrue($ret[$i++] === 1); // hset

        $this->assertTrue($ret[$i++] === FALSE); // get
        $this->assertTrue($ret[$i++] === FALSE); // getset
        $this->assertTrue($ret[$i++] === FALSE); // append
        $this->assertTrue($ret[$i++] === FALSE); // getRange
        $this->assertTrue(is_array($ret[$i]) && count($ret[$i]) === 1 && $ret[$i][0] === FALSE); // mget
        $i++;
        $this->assertTrue($ret[$i++] === FALSE); // incr
        $this->assertTrue($ret[$i++] === FALSE); // incrBy
        $this->assertTrue($ret[$i++] === FALSE); // decr
        $this->assertTrue($ret[$i++] === FALSE); // decrBy

        $this->assertTrue($ret[$i++] === FALSE); // rpush
        $this->assertTrue($ret[$i++] === FALSE); // lpush
        $this->assertTrue($ret[$i++] === FALSE); // llen
        $this->assertTrue($ret[$i++] === FALSE); // lpop
        $this->assertTrue($ret[$i++] === FALSE); // lrange
        $this->assertTrue($ret[$i++] === FALSE); // ltrim
        $this->assertTrue($ret[$i++] === FALSE); // lindex
        $this->assertTrue($ret[$i++] === FALSE); // lset
        $this->assertTrue($ret[$i++] === FALSE); // lremove
        $this->assertTrue($ret[$i++] === FALSE); // lpop
        $this->assertTrue($ret[$i++] === FALSE); // rpop
        $this->assertTrue($ret[$i++] === FALSE); // rpoplush

        $this->assertTrue($ret[$i++] === FALSE); // sadd
        $this->assertTrue($ret[$i++] === FALSE); // sremove
        $this->assertTrue($ret[$i++] === FALSE); // spop
        $this->assertTrue($ret[$i++] === FALSE); // smove
        $this->assertTrue($ret[$i++] === FALSE); // scard
        $this->assertTrue($ret[$i++] === FALSE); // sismember
        $this->assertTrue($ret[$i++] === FALSE); // sinter
        $this->assertTrue($ret[$i++] === FALSE); // sunion
        $this->assertTrue($ret[$i++] === FALSE); // sdiff
        $this->assertTrue($ret[$i++] === FALSE); // smembers
        $this->assertTrue($ret[$i++] === FALSE); // srandmember

        $this->assertTrue($ret[$i++] === FALSE); // zadd
        $this->assertTrue($ret[$i++] === FALSE); // zrem
        $this->assertTrue($ret[$i++] === FALSE); // zincrby
        $this->assertTrue($ret[$i++] === FALSE); // zrank
        $this->assertTrue($ret[$i++] === FALSE); // zrevrank
        $this->assertTrue($ret[$i++] === FALSE); // zrange
        $this->assertTrue($ret[$i++] === FALSE); // zreverserange
        $this->assertTrue($ret[$i++] === FALSE); // zrangebyscore
        $this->assertTrue($ret[$i++] === FALSE); // zcount
        $this->assertTrue($ret[$i++] === FALSE); // zcard
        $this->assertTrue($ret[$i++] === FALSE); // zscore
        $this->assertTrue($ret[$i++] === FALSE); // zremrangebyrank
        $this->assertTrue($ret[$i++] === FALSE); // zremrangebyscore

        $this->assertEquals($i, count($ret));
    }

    public function testDifferentTypeString() {
        $key = '{hash}string';
        $dkey = '{hash}' . __FUNCTION__;

        $this->redis->del($key);
        $this->assertEquals(TRUE, $this->redis->set($key, 'value'));

        // lists I/F
        $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue'));
        $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue'));
        $this->assertEquals(FALSE, $this->redis->lLen($key));
        $this->assertEquals(FALSE, $this->redis->lPop($key));
        $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1));
        $this->assertEquals(FALSE, $this->redis->lIndex($key, 0));
        $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue"));
        $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1));
        $this->assertEquals(FALSE, $this->redis->lPop($key));
        $this->assertEquals(FALSE, $this->redis->rPop($key));
        $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1'));

        // sets I/F
        $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->sPop($key));
        $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->scard($key));
        $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey. 'skey2'));
        $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4'));
        $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7'));
        $this->assertEquals(FALSE, $this->redis->sMembers($key));
        $this->assertEquals(FALSE, $this->redis->sRandMember($key));

        // sorted sets I/F
        $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2));
        $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zCard($key));
        $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2));
        $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2));

        // hash I/F
        $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1'));
        $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1'));
        $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1']));
        $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1']));
        $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1));
        $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2'));
        $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2'));
        $this->assertEquals(FALSE, $this->redis->hLen($key));
        $this->assertEquals(FALSE, $this->redis->hKeys($key));
        $this->assertEquals(FALSE, $this->redis->hVals($key));
        $this->assertEquals(FALSE, $this->redis->hGetAll($key));
    }

    public function testDifferentTypeList() {
        $key = '{hash}list';
        $dkey = '{hash}' . __FUNCTION__;

        $this->redis->del($key);
        $this->assertEquals(1, $this->redis->lPush($key, 'value'));

        // string I/F
        $this->assertEquals(FALSE, $this->redis->get($key));
        $this->assertEquals(FALSE, $this->redis->getset($key, 'value2'));
        $this->assertEquals(FALSE, $this->redis->append($key, 'append'));
        $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8));
        $this->assertEquals([FALSE], $this->redis->mget([$key]));
        $this->assertEquals(FALSE, $this->redis->incr($key));
        $this->assertEquals(FALSE, $this->redis->incrBy($key, 1));
        $this->assertEquals(FALSE, $this->redis->decr($key));
        $this->assertEquals(FALSE, $this->redis->decrBy($key, 1));

        // sets I/F
        $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->sPop($key));
        $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->scard($key));
        $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey . 'skey2'));
        $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4'));
        $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7'));
        $this->assertEquals(FALSE, $this->redis->sMembers($key));
        $this->assertEquals(FALSE, $this->redis->sRandMember($key));

        // sorted sets I/F
        $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2));
        $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zCard($key));
        $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2));
        $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2));

        // hash I/F
        $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1'));
        $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1'));
        $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1']));
        $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1']));
        $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1));
        $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2'));
        $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2'));
        $this->assertEquals(FALSE, $this->redis->hLen($key));
        $this->assertEquals(FALSE, $this->redis->hKeys($key));
        $this->assertEquals(FALSE, $this->redis->hVals($key));
        $this->assertEquals(FALSE, $this->redis->hGetAll($key));
    }

    public function testDifferentTypeSet() {
        $key = '{hash}set';
        $dkey = '{hash}' . __FUNCTION__;
        $this->redis->del($key);
        $this->assertEquals(1, $this->redis->sAdd($key, 'value'));

        // string I/F
        $this->assertEquals(FALSE, $this->redis->get($key));
        $this->assertEquals(FALSE, $this->redis->getset($key, 'value2'));
        $this->assertEquals(FALSE, $this->redis->append($key, 'append'));
        $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8));
        $this->assertEquals([FALSE], $this->redis->mget([$key]));
        $this->assertEquals(FALSE, $this->redis->incr($key));
        $this->assertEquals(FALSE, $this->redis->incrBy($key, 1));
        $this->assertEquals(FALSE, $this->redis->decr($key));
        $this->assertEquals(FALSE, $this->redis->decrBy($key, 1));

        // lists I/F
        $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue'));
        $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue'));
        $this->assertEquals(FALSE, $this->redis->lLen($key));
        $this->assertEquals(FALSE, $this->redis->lPop($key));
        $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1));
        $this->assertEquals(FALSE, $this->redis->lIndex($key, 0));
        $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue"));
        $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1));
        $this->assertEquals(FALSE, $this->redis->lPop($key));
        $this->assertEquals(FALSE, $this->redis->rPop($key));
        $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey  . 'lkey1'));

        // sorted sets I/F
        $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2));
        $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zCard($key));
        $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2));
        $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2));

        // hash I/F
        $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1'));
        $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1'));
        $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1']));
        $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1']));
        $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1));
        $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2'));
        $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2'));
        $this->assertEquals(FALSE, $this->redis->hLen($key));
        $this->assertEquals(FALSE, $this->redis->hKeys($key));
        $this->assertEquals(FALSE, $this->redis->hVals($key));
        $this->assertEquals(FALSE, $this->redis->hGetAll($key));
    }

    public function testDifferentTypeSortedSet() {
        $key = '{hash}sortedset';
        $dkey = '{hash}' . __FUNCTION__;

        $this->redis->del($key);
        $this->assertEquals(1, $this->redis->zAdd($key, 0, 'value'));

        // string I/F
        $this->assertEquals(FALSE, $this->redis->get($key));
        $this->assertEquals(FALSE, $this->redis->getset($key, 'value2'));
        $this->assertEquals(FALSE, $this->redis->append($key, 'append'));
        $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8));
        $this->assertEquals([FALSE], $this->redis->mget([$key]));
        $this->assertEquals(FALSE, $this->redis->incr($key));
        $this->assertEquals(FALSE, $this->redis->incrBy($key, 1));
        $this->assertEquals(FALSE, $this->redis->decr($key));
        $this->assertEquals(FALSE, $this->redis->decrBy($key, 1));

        // lists I/F
        $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue'));
        $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue'));
        $this->assertEquals(FALSE, $this->redis->lLen($key));
        $this->assertEquals(FALSE, $this->redis->lPop($key));
        $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1));
        $this->assertEquals(FALSE, $this->redis->lIndex($key, 0));
        $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue"));
        $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1));
        $this->assertEquals(FALSE, $this->redis->lPop($key));
        $this->assertEquals(FALSE, $this->redis->rPop($key));
        $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1'));

        // sets I/F
        $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->sPop($key));
        $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->scard($key));
        $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey . 'skey2'));
        $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4'));
        $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7'));
        $this->assertEquals(FALSE, $this->redis->sMembers($key));
        $this->assertEquals(FALSE, $this->redis->sRandMember($key));

        // hash I/F
        $this->assertEquals(FALSE, $this->redis->hSet($key, 'key1', 'value1'));
        $this->assertEquals(FALSE, $this->redis->hGet($key, 'key1'));
        $this->assertEquals(FALSE, $this->redis->hMGet($key, ['key1']));
        $this->assertEquals(FALSE, $this->redis->hMSet($key, ['key1' => 'value1']));
        $this->assertEquals(FALSE, $this->redis->hIncrBy($key, 'key2', 1));
        $this->assertEquals(FALSE, $this->redis->hExists($key, 'key2'));
        $this->assertEquals(FALSE, $this->redis->hDel($key, 'key2'));
        $this->assertEquals(FALSE, $this->redis->hLen($key));
        $this->assertEquals(FALSE, $this->redis->hKeys($key));
        $this->assertEquals(FALSE, $this->redis->hVals($key));
        $this->assertEquals(FALSE, $this->redis->hGetAll($key));
    }

    public function testDifferentTypeHash() {
        $key = '{hash}hash';
        $dkey = '{hash}hash';

        $this->redis->del($key);
        $this->assertEquals(1, $this->redis->hSet($key, 'key', 'value'));

        // string I/F
        $this->assertEquals(FALSE, $this->redis->get($key));
        $this->assertEquals(FALSE, $this->redis->getset($key, 'value2'));
        $this->assertEquals(FALSE, $this->redis->append($key, 'append'));
        $this->assertEquals(FALSE, $this->redis->getRange($key, 0, 8));
        $this->assertEquals([FALSE], $this->redis->mget([$key]));
        $this->assertEquals(FALSE, $this->redis->incr($key));
        $this->assertEquals(FALSE, $this->redis->incrBy($key, 1));
        $this->assertEquals(FALSE, $this->redis->decr($key));
        $this->assertEquals(FALSE, $this->redis->decrBy($key, 1));

        // lists I/F
        $this->assertEquals(FALSE, $this->redis->rPush($key, 'lvalue'));
        $this->assertEquals(FALSE, $this->redis->lPush($key, 'lvalue'));
        $this->assertEquals(FALSE, $this->redis->lLen($key));
        $this->assertEquals(FALSE, $this->redis->lPop($key));
        $this->assertEquals(FALSE, $this->redis->lrange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->lTrim($key, 0, 1));
        $this->assertEquals(FALSE, $this->redis->lIndex($key, 0));
        $this->assertEquals(FALSE, $this->redis->lSet($key, 0, "newValue"));
        $this->assertEquals(FALSE, $this->redis->lrem($key, 'lvalue', 1));
        $this->assertEquals(FALSE, $this->redis->lPop($key));
        $this->assertEquals(FALSE, $this->redis->rPop($key));
        $this->assertEquals(FALSE, $this->redis->rPoplPush($key, $dkey . 'lkey1'));

        // sets I/F
        $this->assertEquals(FALSE, $this->redis->sAdd($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->srem($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->sPop($key));
        $this->assertEquals(FALSE, $this->redis->sMove($key, $dkey . 'skey1', 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->scard($key));
        $this->assertEquals(FALSE, $this->redis->sismember($key, 'sValue1'));
        $this->assertEquals(FALSE, $this->redis->sInter($key, $dkey . 'skey2'));
        $this->assertEquals(FALSE, $this->redis->sUnion($key, $dkey . 'skey4'));
        $this->assertEquals(FALSE, $this->redis->sDiff($key, $dkey . 'skey7'));
        $this->assertEquals(FALSE, $this->redis->sMembers($key));
        $this->assertEquals(FALSE, $this->redis->sRandMember($key));

        // sorted sets I/F
        $this->assertEquals(FALSE, $this->redis->zAdd($key, 1, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRem($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zIncrBy($key, 1, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRank($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRevRank($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zRevRange($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zRangeByScore($key, 1, 2));
        $this->assertEquals(FALSE, $this->redis->zCount($key, 0, -1));
        $this->assertEquals(FALSE, $this->redis->zCard($key));
        $this->assertEquals(FALSE, $this->redis->zScore($key, 'zValue1'));
        $this->assertEquals(FALSE, $this->redis->zRemRangeByRank($key, 1, 2));
        $this->assertEquals(FALSE, $this->redis->zRemRangeByScore($key, 1, 2));
    }

    public function testSerializerPHP() {
        $this->checkSerializer(Redis::SERIALIZER_PHP);

        // with prefix
        $this->redis->setOption(Redis::OPT_PREFIX, "test:");
        $this->checkSerializer(Redis::SERIALIZER_PHP);
        $this->redis->setOption(Redis::OPT_PREFIX, "");
    }

    public function testSerializerIGBinary() {
        if(defined('Redis::SERIALIZER_IGBINARY')) {
            $this->checkSerializer(Redis::SERIALIZER_IGBINARY);

            // with prefix
            $this->redis->setOption(Redis::OPT_PREFIX, "test:");
            $this->checkSerializer(Redis::SERIALIZER_IGBINARY);
            $this->redis->setOption(Redis::OPT_PREFIX, "");
        }
    }

    public function testSerializerMsgPack() {
        if(defined('Redis::SERIALIZER_MSGPACK')) {
            $this->checkSerializer(Redis::SERIALIZER_MSGPACK);

            // with prefix
            $this->redis->setOption(Redis::OPT_PREFIX, "test:");
            $this->checkSerializer(Redis::SERIALIZER_MSGPACK);
            $this->redis->setOption(Redis::OPT_PREFIX, "");
        }
    }

    public function testSerializerJSON()
    {
        $this->checkSerializer(Redis::SERIALIZER_JSON);

        // with prefix
        $this->redis->setOption(Redis::OPT_PREFIX, "test:");
        $this->checkSerializer(Redis::SERIALIZER_JSON);
        $this->redis->setOption(Redis::OPT_PREFIX, "");
    }

    private function checkSerializer($mode) {

        $this->redis->del('key');
        $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE);   // default

        $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, $mode) === TRUE);  // set ok
        $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === $mode);    // get ok

        // lPush, rPush
        $a = ['hello world', 42, TRUE, ['<tag>' => 1729]];
        $this->redis->del('key');
        $this->redis->lPush('key', $a[0]);
        $this->redis->rPush('key', $a[1]);
        $this->redis->rPush('key', $a[2]);
        $this->redis->rPush('key', $a[3]);

        // lrange
        $this->assertTrue($a === $this->redis->lrange('key', 0, -1));

        // lIndex
        $this->assertTrue($a[0] === $this->redis->lIndex('key', 0));
        $this->assertTrue($a[1] === $this->redis->lIndex('key', 1));
        $this->assertTrue($a[2] === $this->redis->lIndex('key', 2));
        $this->assertTrue($a[3] === $this->redis->lIndex('key', 3));

        // lrem
        $this->assertTrue($this->redis->lrem('key', $a[3]) === 1);
        $this->assertTrue(array_slice($a, 0, 3) === $this->redis->lrange('key', 0, -1));

        // lSet
        $a[0] = ['k' => 'v']; // update
        $this->assertTrue(TRUE === $this->redis->lSet('key', 0, $a[0]));
        $this->assertTrue($a[0] === $this->redis->lIndex('key', 0));

        // lInsert
        $this->assertTrue($this->redis->lInsert('key', Redis::BEFORE, $a[0], [1,2,3]) === 4);
        $this->assertTrue($this->redis->lInsert('key', Redis::AFTER, $a[0], [4,5,6]) === 5);

        $a = [[1,2,3], $a[0], [4,5,6], $a[1], $a[2]];
        $this->assertTrue($a === $this->redis->lrange('key', 0, -1));

        // sAdd
        $this->redis->del('{set}key');
        $s = [1,'a', [1,2,3], ['k' => 'v']];

        $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[0]));
        $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[1]));
        $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[2]));
        $this->assertTrue(1 === $this->redis->sAdd('{set}key', $s[3]));

        // variadic sAdd
        $this->redis->del('k');
        $this->assertTrue(3 === $this->redis->sAdd('k', 'a', 'b', 'c'));
        $this->assertTrue(1 === $this->redis->sAdd('k', 'a', 'b', 'c', 'd'));

        // srem
        $this->assertTrue(1 === $this->redis->srem('{set}key', $s[3]));
        $this->assertTrue(0 === $this->redis->srem('{set}key', $s[3]));

        // variadic
        $this->redis->del('k');
        $this->redis->sAdd('k', 'a', 'b', 'c', 'd');
        $this->assertTrue(2 === $this->redis->sRem('k', 'a', 'd'));
        $this->assertTrue(2 === $this->redis->sRem('k', 'b', 'c', 'e'));
        $this->assertEquals(0, $this->redis->exists('k'));

        // sismember
        $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[0]));
        $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[1]));
        $this->assertTrue(TRUE === $this->redis->sismember('{set}key', $s[2]));
        $this->assertTrue(FALSE === $this->redis->sismember('{set}key', $s[3]));
        unset($s[3]);

        // sMove
        $this->redis->del('{set}tmp');
        $this->redis->sMove('{set}key', '{set}tmp', $s[0]);
        $this->assertTrue(FALSE === $this->redis->sismember('{set}key', $s[0]));
        $this->assertTrue(TRUE === $this->redis->sismember('{set}tmp', $s[0]));
        unset($s[0]);

        // sorted sets
        $z = ['z0', ['k' => 'v'], FALSE, NULL];
        $this->redis->del('key');

        // zAdd
        $this->assertTrue(1 === $this->redis->zAdd('key', 0, $z[0]));
        $this->assertTrue(1 === $this->redis->zAdd('key', 1, $z[1]));
        $this->assertTrue(1 === $this->redis->zAdd('key', 2, $z[2]));
        $this->assertTrue(1 === $this->redis->zAdd('key', 3, $z[3]));

        // zRem
        $this->assertTrue(1 === $this->redis->zRem('key', $z[3]));
        $this->assertTrue(0 === $this->redis->zRem('key', $z[3]));
        unset($z[3]);

        // check that zRem doesn't crash with a missing parameter (GitHub issue #102):
        $this->assertTrue(FALSE === @$this->redis->zRem('key'));

        // variadic
        $this->redis->del('k');
        $this->redis->zAdd('k', 0, 'a');
        $this->redis->zAdd('k', 1, 'b');
        $this->redis->zAdd('k', 2, 'c');
        $this->assertTrue(2 === $this->redis->zRem('k', 'a', 'c'));
        $this->assertTrue(1.0 === $this->redis->zScore('k', 'b'));
        $this->assertTrue($this->redis->zRange('k', 0, -1, true) == ['b' => 1.0]);

        // zRange
        $this->assertTrue($z === $this->redis->zRange('key', 0, -1));

        // zScore
        $this->assertTrue(0.0 === $this->redis->zScore('key', $z[0]));
        $this->assertTrue(1.0 === $this->redis->zScore('key', $z[1]));
        $this->assertTrue(2.0 === $this->redis->zScore('key', $z[2]));

        // zRank
        $this->assertTrue(0 === $this->redis->zRank('key', $z[0]));
        $this->assertTrue(1 === $this->redis->zRank('key', $z[1]));
        $this->assertTrue(2 === $this->redis->zRank('key', $z[2]));

        // zRevRank
        $this->assertTrue(2 === $this->redis->zRevRank('key', $z[0]));
        $this->assertTrue(1 === $this->redis->zRevRank('key', $z[1]));
        $this->assertTrue(0 === $this->redis->zRevRank('key', $z[2]));

        // zIncrBy
        $this->assertTrue(3.0 === $this->redis->zIncrBy('key', 1.0, $z[2]));
        $this->assertTrue(3.0 === $this->redis->zScore('key', $z[2]));

        $this->assertTrue(5.0 === $this->redis->zIncrBy('key', 2.0, $z[2]));
        $this->assertTrue(5.0 === $this->redis->zScore('key', $z[2]));

        $this->assertTrue(2.0 === $this->redis->zIncrBy('key', -3.0, $z[2]));
        $this->assertTrue(2.0 === $this->redis->zScore('key', $z[2]));

        // mset
        $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']];
        $this->assertTrue(TRUE === $this->redis->mset($a));
        foreach($a as $k => $v) {
            $this->assertTrue($this->redis->get($k) === $v);
        }

        $a = ['k0' => 1, 'k1' => 42, 'k2' => NULL, 'k3' => FALSE, 'k4' => ['a' => 'b']];

        // hSet
        $this->redis->del('key');
        foreach($a as $k => $v) {
            $this->assertTrue(1 === $this->redis->hSet('key', $k, $v));
        }

        // hGet
        foreach($a as $k => $v) {
            $this->assertTrue($v === $this->redis->hGet('key', $k));
        }

        // hGetAll
        $this->assertTrue($a === $this->redis->hGetAll('key'));
        $this->assertTrue(TRUE === $this->redis->hExists('key', 'k0'));
        $this->assertTrue(TRUE === $this->redis->hExists('key', 'k1'));
        $this->assertTrue(TRUE === $this->redis->hExists('key', 'k2'));
        $this->assertTrue(TRUE === $this->redis->hExists('key', 'k3'));
        $this->assertTrue(TRUE === $this->redis->hExists('key', 'k4'));

        // hMSet
        $this->redis->del('key');
        $this->redis->hMSet('key', $a);
        foreach($a as $k => $v) {
            $this->assertTrue($v === $this->redis->hGet('key', $k));
        }

        // hMget
        $hmget = $this->redis->hMget('key', array_keys($a));
        foreach($hmget as $k => $v) {
            $this->assertTrue($v === $a[$k]);
        }

        // getMultiple
        $this->redis->set('a', NULL);
        $this->redis->set('b', FALSE);
        $this->redis->set('c', 42);
        $this->redis->set('d', ['x' => 'y']);

        $this->assertTrue([NULL, FALSE, 42, ['x' => 'y']] === $this->redis->mGet(['a', 'b', 'c', 'd']));

        // pipeline
        if ($this->havePipeline()) {
            $this->sequence(Redis::PIPELINE);
        }

        // multi-exec
        $this->sequence(Redis::MULTI);

        // keys
        $this->assertTrue(is_array($this->redis->keys('*')));

        // issue #62, hgetall
        $this->redis->del('hash1');
        $this->redis->hSet('hash1','data', 'test 1');
        $this->redis->hSet('hash1','session_id', 'test 2');

        $data = $this->redis->hGetAll('hash1');
        $this->assertTrue($data['data'] === 'test 1');
        $this->assertTrue($data['session_id'] === 'test 2');

        // issue #145, serializer with objects.
        $this->redis->set('x', [new stdClass, new stdClass]);
        $x = $this->redis->get('x');
        $this->assertTrue(is_array($x));
        if ($mode === Redis::SERIALIZER_JSON) {
            $this->assertTrue(is_array($x[0]));
            $this->assertTrue(is_array($x[1]));
        } else {
            $this->assertTrue(is_object($x[0]) && get_class($x[0]) === 'stdClass');
            $this->assertTrue(is_object($x[1]) && get_class($x[1]) === 'stdClass');
        }

        // revert
        $this->assertTrue($this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE) === TRUE);     // set ok
        $this->assertTrue($this->redis->getOption(Redis::OPT_SERIALIZER) === Redis::SERIALIZER_NONE);       // get ok
    }

    public function testCompressionLZF()
    {
        if (!defined('Redis::COMPRESSION_LZF')) {
            $this->markTestSkipped();
        }

        /* Don't crash on improperly compressed LZF data */
        $payload = 'not-actually-lzf-compressed';
        $this->redis->set('badlzf', $payload);
        $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_LZF);
        $this->assertEquals($payload, $this->redis->get('badlzf'));
        $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);

        $this->checkCompression(Redis::COMPRESSION_LZF, 0);
    }

    public function testCompressionZSTD()
    {
        if (!defined('Redis::COMPRESSION_ZSTD')) {
            $this->markTestSkipped();
        }

        /* Issue 1936 regression.  Make sure we don't overflow on bad data */
        $this->redis->del('badzstd');
        $this->redis->set('badzstd', '123');
        $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_ZSTD);
        $this->assertEquals('123', $this->redis->get('badzstd'));
        $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);

        $this->checkCompression(Redis::COMPRESSION_ZSTD, 0);
        $this->checkCompression(Redis::COMPRESSION_ZSTD, 9);
    }


    public function testCompressionLZ4()
    {
        if (!defined('Redis::COMPRESSION_LZ4')) {
            $this->markTestSkipped();
        }
        $this->checkCompression(Redis::COMPRESSION_LZ4, 0);
        $this->checkCompression(Redis::COMPRESSION_LZ4, 9);
    }

    private function checkCompression($mode, $level)
    {
        $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION, $mode) === TRUE);  // set ok
        $this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION) === $mode);    // get ok

        $this->assertTrue($this->redis->setOption(Redis::OPT_COMPRESSION_LEVEL, $level) === TRUE);
        $this->assertTrue($this->redis->getOption(Redis::OPT_COMPRESSION_LEVEL) === $level);

        $val = 'xxxxxxxxxx';
        $this->redis->set('key', $val);
        $this->assertEquals($val, $this->redis->get('key'));

        /* Empty data */
        $this->redis->set('key', '');
        $this->assertEquals('', $this->redis->get('key'));

        /* Iterate through class sizes */
        for ($i = 1; $i <= 65536; $i *= 2) {
            foreach ([str_repeat('A', $i), random_bytes($i)] as $val) {
                $this->redis->set('key', $val);
                $this->assertEquals($val, $this->redis->get('key'));
            }
        }

        // Issue 1945. Ensure we decompress data with hmget.
        $this->redis->hset('hkey', 'data', 'abc');
        $this->assertEquals('abc', current($this->redis->hmget('hkey', ['data'])));
    }

    public function testDumpRestore() {

        if (version_compare($this->version, "2.5.0") < 0) {
            $this->markTestSkipped();
        }

        $this->redis->del('foo');
        $this->redis->del('bar');

        $this->redis->set('foo', 'this-is-foo');
        $this->redis->set('bar', 'this-is-bar');

        $d_foo = $this->redis->dump('foo');
        $d_bar = $this->redis->dump('bar');

        $this->redis->del('foo');
        $this->redis->del('bar');

        // Assert returns from restore
        $this->assertTrue($this->redis->restore('foo', 0, $d_bar));
        $this->assertTrue($this->redis->restore('bar', 0, $d_foo));

        // Now check that the keys have switched
        $this->assertTrue($this->redis->get('foo') === 'this-is-bar');
        $this->assertTrue($this->redis->get('bar') === 'this-is-foo');

        $this->redis->del('foo');
        $this->redis->del('bar');
    }

    public function testGetLastError() {
        // We shouldn't have any errors now
        $this->assertTrue($this->redis->getLastError() === NULL);

        // test getLastError with a regular command
        $this->redis->set('x', 'a');
        $this->assertFalse($this->redis->incr('x'));
        $incrError = $this->redis->getLastError();
        $this->assertTrue(strlen($incrError) > 0);

        // clear error
        $this->redis->clearLastError();
        $this->assertTrue($this->redis->getLastError() === NULL);
    }

    // Helper function to compare nested results -- from the php.net array_diff page, I believe
    private function array_diff_recursive($aArray1, $aArray2) {
        $aReturn = [];

        foreach ($aArray1 as $mKey => $mValue) {
            if (array_key_exists($mKey, $aArray2)) {
                if (is_array($mValue)) {
                    $aRecursiveDiff = $this->array_diff_recursive($mValue, $aArray2[$mKey]);
                    if (count($aRecursiveDiff)) {
                        $aReturn[$mKey] = $aRecursiveDiff;
                    }
                } else {
                    if ($mValue != $aArray2[$mKey]) {
                        $aReturn[$mKey] = $mValue;
                    }
                }
            } else {
                $aReturn[$mKey] = $mValue;
            }
        }

        return $aReturn;
    }

    public function testScript() {

        if (version_compare($this->version, "2.5.0") < 0) {
            $this->markTestSkipped();
        }

        // Flush any scripts we have
        $this->assertTrue($this->redis->script('flush'));

        // Silly scripts to test against
        $s1_src = 'return 1';
        $s1_sha = sha1($s1_src);
        $s2_src = 'return 2';
        $s2_sha = sha1($s2_src);
        $s3_src = 'return 3';
        $s3_sha = sha1($s3_src);

        // None should exist
        $result = $this->redis->script('exists', $s1_sha, $s2_sha, $s3_sha);
        $this->assertTrue(is_array($result) && count($result) == 3);
        $this->assertTrue(is_array($result) && count(array_filter($result)) == 0);

        // Load them up
        $this->assertTrue($this->redis->script('load', $s1_src) == $s1_sha);
        $this->assertTrue($this->redis->script('load', $s2_src) == $s2_sha);
        $this->assertTrue($this->redis->script('load', $s3_src) == $s3_sha);

        // They should all exist
        $result = $this->redis->script('exists', $s1_sha, $s2_sha, $s3_sha);
        $this->assertTrue(is_array($result) && count(array_filter($result)) == 3);
    }

    public function testEval() {

        if (version_compare($this->version, "2.5.0") < 0) {
            $this->markTestSkipped();
        }

        // Basic single line response tests
        $this->assertTrue(1 == $this->redis->eval('return 1'));
        $this->assertTrue(1.55 == $this->redis->eval("return '1.55'"));
        $this->assertTrue("hello, world" == $this->redis->eval("return 'hello, world'"));

        /*
         * Keys to be incorporated into lua results
         */
        // Make a list
        $this->redis->del('{eval-key}-list');
        $this->redis->rpush('{eval-key}-list', 'a');
        $this->redis->rpush('{eval-key}-list', 'b');
        $this->redis->rpush('{eval-key}-list', 'c');

        // Make a set
        $this->redis->del('{eval-key}-zset');
        $this->redis->zadd('{eval-key}-zset', 0, 'd');
        $this->redis->zadd('{eval-key}-zset', 1, 'e');
        $this->redis->zadd('{eval-key}-zset', 2, 'f');

        // Basic keys
        $this->redis->set('{eval-key}-str1', 'hello, world');
        $this->redis->set('{eval-key}-str2', 'hello again!');

        // Use a script to return our list, and verify its response
        $list = $this->redis->eval("return redis.call('lrange', KEYS[1], 0, -1)", ['{eval-key}-list'], 1);
        $this->assertTrue($list === ['a','b','c']);

        // Use a script to return our zset
        $zset = $this->redis->eval("return redis.call('zrange', KEYS[1], 0, -1)", ['{eval-key}-zset'], 1);
        $this->assertTrue($zset == ['d','e','f']);

        // Test an empty MULTI BULK response
        $this->redis->del('{eval-key}-nolist');
        $empty_resp = $this->redis->eval("return redis.call('lrange', '{eval-key}-nolist', 0, -1)",
            ['{eval-key}-nolist'], 1);
        $this->assertTrue(is_array($empty_resp) && empty($empty_resp));

        // Now test a nested reply
        $nested_script = "
            return {
                1,2,3, {
                    redis.call('get', '{eval-key}-str1'),
                    redis.call('get', '{eval-key}-str2'),
                    redis.call('lrange', 'not-any-kind-of-list', 0, -1),
                    {
                        redis.call('zrange','{eval-key}-zset', 0, -1),
                        redis.call('lrange', '{eval-key}-list', 0, -1)
                    }
                }
            }
        ";

        $expected = [
            1, 2, 3, [
                'hello, world',
                'hello again!',
                [],
                [
                    ['d','e','f'],
                    ['a','b','c']
                ]
            ]
        ];

        // Now run our script, and check our values against each other
        $eval_result = $this->redis->eval($nested_script, ['{eval-key}-str1', '{eval-key}-str2', '{eval-key}-zset', '{eval-key}-list'], 4);
        $this->assertTrue(is_array($eval_result) && count($this->array_diff_recursive($eval_result, $expected)) == 0);

        /*
         * Nested reply wihin a multi/pipeline block
         */

        $num_scripts = 10;

        $arr_modes = [Redis::MULTI];
        if ($this->havePipeline()) $arr_modes[] = Redis::PIPELINE;

        foreach($arr_modes as $mode) {
            $this->redis->multi($mode);
            for($i=0;$i<$num_scripts;$i++) {
                $this->redis->eval($nested_script, ['{eval-key}-dummy'], 1);
            }
            $replies = $this->redis->exec();

            foreach($replies as $reply) {
                $this->assertTrue(is_array($reply) && count($this->array_diff_recursive($reply, $expected)) == 0);
            }
        }

        /*
         * KEYS/ARGV
         */

        $args_script = "return {KEYS[1],KEYS[2],KEYS[3],ARGV[1],ARGV[2],ARGV[3]}";
        $args_args   = ['{k}1','{k}2','{k}3','v1','v2','v3'];
        $args_result = $this->redis->eval($args_script, $args_args, 3);
        $this->assertTrue($args_result === $args_args);

        // turn on key prefixing
        $this->redis->setOption(Redis::OPT_PREFIX, 'prefix:');
        $args_result = $this->redis->eval($args_script, $args_args, 3);

        // Make sure our first three are prefixed
        for($i=0;$i<count($args_result);$i++) {
            if($i<3) {
                // Should be prefixed
                $this->assertTrue($args_result[$i] == 'prefix:' . $args_args[$i]);
            } else {
                // Should not be prefixed
                $this->assertTrue($args_result[$i] == $args_args[$i]);
            }
        }
    }

    public function testEvalSHA() {
        if (version_compare($this->version, "2.5.0") < 0) {
            $this->markTestSkipped();
        }

        // Flush any loaded scripts
        $this->redis->script('flush');

        // Non existant script (but proper sha1), and a random (not) sha1 string
        $this->assertFalse($this->redis->evalsha(sha1(uniqid())));
        $this->assertFalse($this->redis->evalsha('some-random-data'));

        // Load a script
        $cb  = uniqid(); // To ensure the script is new
        $scr = "local cb='$cb' return 1";
        $sha = sha1($scr);

        // Run it when it doesn't exist, run it with eval, and then run it with sha1
        $this->assertTrue(false === $this->redis->evalsha($scr));
        $this->assertTrue(1 === $this->redis->eval($scr));
        $this->assertTrue(1 === $this->redis->evalsha($sha));
    }

    public function testSerialize() {
        $vals = [1, 1.5, 'one', ['here','is','an','array']];

        // Test with no serialization at all
        $this->assertTrue($this->redis->_serialize('test') === 'test');
        $this->assertTrue($this->redis->_serialize(1) === '1');
        $this->assertTrue($this->redis->_serialize([]) === 'Array');
        $this->assertTrue($this->redis->_serialize(new stdClass) === 'Object');

        $arr_serializers = [Redis::SERIALIZER_PHP];
        if(defined('Redis::SERIALIZER_IGBINARY')) {
            $arr_serializers[] = Redis::SERIALIZER_IGBINARY;
        }

        if(defined('Redis::SERIALIZER_MSGPACK')) {
            $arr_serializers[] = Redis::SERIALIZER_MSGPACK;
        }

        foreach($arr_serializers as $mode) {
            $arr_enc = [];
            $arr_dec = [];

            foreach($vals as $k => $v) {
                $enc = $this->redis->_serialize($v);
                $dec = $this->redis->_unserialize($enc);

                // They should be the same
                $this->assertTrue($enc == $dec);
            }
        }
    }

    public function testUnserialize() {
        $vals = [
            1,1.5,'one',['this','is','an','array']
        ];

        $serializers = Array(Redis::SERIALIZER_PHP);

        if(defined('Redis::SERIALIZER_IGBINARY')) {
            $serializers[] = Redis::SERIALIZER_IGBINARY;
        }

        if(defined('Redis::SERIALIZER_MSGPACK')) {
            $serializers[] = Redis::SERIALIZER_MSGPACK;
        }

        foreach($serializers as $mode) {
            $vals_enc = [];

            // Pass them through redis so they're serialized
            foreach($vals as $key => $val) {
                $this->redis->setOption(Redis::OPT_SERIALIZER, $mode);

                $key = "key" . ++$key;
                $this->redis->del($key);
                $this->redis->set($key, $val);

                // Clear serializer, get serialized value
                $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
                $vals_enc[] = $this->redis->get($key);
            }

            // Run through our array comparing values
            for($i=0;$i<count($vals);$i++) {
                // reset serializer
                $this->redis->setOption(Redis::OPT_SERIALIZER, $mode);
                $this->assertTrue($vals[$i] == $this->redis->_unserialize($vals_enc[$i]));
                $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
            }
        }
    }

    public function testCompressHelpers() {
        $compressors = self::getAvailableCompression();

        $vals = ['foo', 12345, random_bytes(128), ''];

        $oldcmp = $this->redis->getOption(Redis::OPT_COMPRESSION);

        foreach ($compressors as $cmp) {
            foreach ($vals as $val) {
                $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp);
                $this->redis->set('cmpkey', $val);

                /* Get the value raw */
                $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);
                $raw = $this->redis->get('cmpkey');
                $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp);

                $this->assertEquals($raw, $this->redis->_compress($val));

                $uncompressed = $this->redis->get('cmpkey');
                $this->assertEquals($uncompressed, $this->redis->_uncompress($raw));
            }
        }

        $this->redis->setOption(Redis::OPT_COMPRESSION, $oldcmp);
    }

    public function testPackHelpers() {
        list ($oldser, $oldcmp) = [
            $this->redis->getOption(Redis::OPT_SERIALIZER),
            $this->redis->getOption(Redis::OPT_COMPRESSION)
        ];

        foreach ($this->serializers as $ser) {
            $compressors = self::getAvailableCompression();
            foreach ($compressors as $cmp) {
                $this->redis->setOption(Redis::OPT_SERIALIZER, $ser);
                $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp);

		foreach (['foo', 12345, random_bytes(128), '', ['an', 'array']] as $v) {
                    /* Can only attempt the array if we're serializing */
                    if (is_array($v) && $ser == Redis::SERIALIZER_NONE)
                        continue;

                    $this->redis->set('packkey', $v);

                    /* Get the value raw */
                    $this->redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
                    $this->redis->setOption(Redis::OPT_COMPRESSION, Redis::COMPRESSION_NONE);
                    $raw = $this->redis->get('packkey');
                    $this->redis->setOption(Redis::OPT_SERIALIZER, $ser);
                    $this->redis->setOption(Redis::OPT_COMPRESSION, $cmp);

                    $this->assertEquals($raw, $this->redis->_pack($v));

                    $unpacked = $this->redis->get('packkey');
		    $this->assertEquals($unpacked, $this->redis->_unpack($raw));
		}
	    }
        }

        $this->redis->setOption(Redis::OPT_SERIALIZER, $oldser);
        $this->redis->setOption(Redis::OPT_COMPRESSION, $oldcmp);
    }

    public function testPrefix() {
        // no prefix
        $this->redis->setOption(Redis::OPT_PREFIX, '');
        $this->assertTrue('key' == $this->redis->_prefix('key'));

        // with a prefix
        $this->redis->setOption(Redis::OPT_PREFIX, 'some-prefix:');
        $this->assertTrue('some-prefix:key' == $this->redis->_prefix('key'));

        // Clear prefix
        $this->redis->setOption(Redis::OPT_PREFIX, '');

    }

    public function testReplyLiteral() {
        $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false);
        $this->assertTrue($this->redis->rawCommand('set', 'foo', 'bar'));
        $this->assertTrue($this->redis->eval("return redis.call('set', 'foo', 'bar')", [], 0));

        $rv = $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1);
        $this->assertEquals([true, true], $rv);

        $this->redis->setOption(Redis::OPT_REPLY_LITERAL, true);
        $this->assertEquals('OK', $this->redis->rawCommand('set', 'foo', 'bar'));
        $this->assertEquals('OK', $this->redis->eval("return redis.call('set', 'foo', 'bar')", [], 0));

        // Nested
        $rv = $this->redis->eval("return {redis.call('set', KEYS[1], 'bar'), redis.call('ping')}", ['foo'], 1);
        $this->assertEquals(['OK', 'PONG'], $rv);

        // Reset
        $this->redis->setOption(Redis::OPT_REPLY_LITERAL, false);
    }

    /* Test that we can configure PhpRedis to return NULL for *-1 even nestedwithin replies */
    public function testNestedNullArray() {
        $this->redis->del('{notaset}');

        foreach ([false => [], true => NULL] as $opt => $test) {
            $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, $opt);
            $this->assertEquals([$test, $test], $this->redis->geoPos('{notaset}', 'm1', 'm2'));

            $this->redis->multi();
            $this->redis->geoPos('{notaset}', 'm1', 'm2');
            $this->assertEquals([[$test, $test]], $this->redis->exec());
        }

        $this->redis->setOption(Redis::OPT_NULL_MULTIBULK_AS_NULL, false);
    }

    public function testReconnectSelect() {
        $key = 'reconnect-select';
        $value = 'Has been set!';

        $original_cfg = $this->redis->config('GET', 'timeout');

        // Make sure the default DB doesn't have the key.
        $this->redis->select(0);
        $this->redis->del($key);

        // Set the key on a different DB.
        $this->redis->select(5);
        $this->redis->set($key, $value);

        // Time out after 1 second.
        $this->redis->config('SET', 'timeout', '1');

        // Wait for the timeout. With Redis 2.4, we have to wait up to 10 s
        // for the server to close the connection, regardless of the timeout
        // setting.
        sleep(11);

        // Make sure we're still using the same DB.
        $this->assertEquals($value, $this->redis->get($key));

        // Revert the setting.
        $this->redis->config('SET', 'timeout', $original_cfg['timeout']);
    }

    public function testTime() {

        if (version_compare($this->version, "2.5.0") < 0) {
            $this->markTestSkipped();
        }

        $time_arr = $this->redis->time();
        $this->assertTrue(is_array($time_arr) && count($time_arr) == 2 &&
                          strval(intval($time_arr[0])) === strval($time_arr[0]) &&
                          strval(intval($time_arr[1])) === strval($time_arr[1]));
    }

    public function testReadTimeoutOption() {

        $this->assertTrue(defined('Redis::OPT_READ_TIMEOUT'));

        $this->redis->setOption(Redis::OPT_READ_TIMEOUT, "12.3");
        $this->assertEquals(12.3, $this->redis->getOption(Redis::OPT_READ_TIMEOUT));
    }

    public function testIntrospection() {
        // Simple introspection tests
        $this->assertTrue($this->redis->getHost() === $this->getHost());
        $this->assertTrue($this->redis->getPort() === $this->getPort());
        $this->assertTrue($this->redis->getAuth() === $this->getAuth());
    }

    /**
     * Scan and variants
     */

    protected function get_keyspace_count($str_db) {
        $arr_info = $this->redis->info();
        if (isset($arr_info[$str_db])) {
            $arr_info = $arr_info[$str_db];
            $arr_info = explode(',', $arr_info);
            $arr_info = explode('=', $arr_info[0]);
            return $arr_info[1];
        } else {
            return 0;
        }
    }

    public function testScan() {
        if(version_compare($this->version, "2.8.0") < 0) {
            $this->markTestSkipped();
            return;
        }

        // Key count
        $i_key_count = $this->get_keyspace_count('db0');

        // Have scan retry
        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);

        // Scan them all
        $it = NULL;
        while($arr_keys = $this->redis->scan($it)) {
            $i_key_count -= count($arr_keys);
        }
        // Should have iterated all keys
        $this->assertEquals(0, $i_key_count);

        // Unique keys, for pattern matching
        $str_uniq = uniqid() . '-' . uniqid();
        for($i=0;$i<10;$i++) {
            $this->redis->set($str_uniq . "::$i", "bar::$i");
        }

        // Scan just these keys using a pattern match
        $it = NULL;
        while($arr_keys = $this->redis->scan($it, "*$str_uniq*")) {
            $i -= count($arr_keys);
        }
        $this->assertEquals(0, $i);

        // SCAN with type is scheduled for release in Redis 6.
        if (version_compare($this->version, "6.0.0") >= 0) {
            // Use a unique ID so we can find our type keys
            $id = uniqid();

            // Create some simple keys and lists
            for ($i = 0; $i < 3; $i++) {
                $str_simple = "simple:{$id}:$i";
                $str_list = "list:{$id}:$i";

                $this->redis->set($str_simple, $i);
                $this->redis->del($str_list);
                $this->redis->rpush($str_list, ['foo']);

                $arr_keys["STRING"][] = $str_simple;
                $arr_keys["LIST"][] = $str_list;
            }

            // Make sure we can scan for specific types
            foreach ($arr_keys as $str_type => $arr_vals) {
                foreach ([NULL, 10] as $i_count) {
                    $arr_resp = [];

                    $it = NULL;
                    while ($arr_scan = $this->redis->scan($it, "*$id*", $i_count, $str_type)) {
                        $arr_resp = array_merge($arr_resp, $arr_scan);
                    }

                    sort($arr_vals); sort($arr_resp);
                    $this->assertEquals($arr_vals, $arr_resp);
                }
            }
        }
    }

    public function testScanPrefix() {
        $keyid = uniqid();

        /* Set some keys with different prefixes */
        $arr_prefixes = ['prefix-a:', 'prefix-b:'];
        foreach ($arr_prefixes as $str_prefix) {
            $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
            $this->redis->set("$keyid", "LOLWUT");
            $arr_all_keys["${str_prefix}${keyid}"] = true;
        }

        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_PREFIX);

        foreach ($arr_prefixes as $str_prefix) {
            $this->redis->setOption(Redis::OPT_PREFIX, $str_prefix);
            $it = NULL;
            $arr_keys = $this->redis->scan($it, "*$keyid*");
            $this->assertEquals($arr_keys, ["${str_prefix}${keyid}"]);
        }

        /* Unset the prefix option */
        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);

        $it = NULL;
        while ($arr_keys = $this->redis->scan($it, "*$keyid*")) {
            foreach ($arr_keys as $str_key) {
                unset($arr_all_keys[$str_key]);
            }
        }

        /* Should have touched every key */
        $this->assertTrue(count($arr_all_keys) == 0);
    }

    public function testMaxRetriesOption() {
        $maxRetriesExpected = 5;
        $this->redis->setOption(Redis::OPT_MAX_RETRIES, $maxRetriesExpected);
        $maxRetriesActual=$this->redis->getOption(Redis::OPT_MAX_RETRIES);
        $this->assertEquals($maxRetriesActual, $maxRetriesExpected);
    }

    public function testBackoffOptions() {
        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DEFAULT);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DEFAULT);

        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_CONSTANT);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_CONSTANT);

        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_UNIFORM);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_UNIFORM);

        $this->redis -> setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EXPONENTIAL);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EXPONENTIAL);

        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_EQUAL_JITTER);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_EQUAL_JITTER);

        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_FULL_JITTER);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_FULL_JITTER);

        $this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_ALGORITHM), Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER);

        $this->assertFalse($this->redis->setOption(Redis::OPT_BACKOFF_ALGORITHM, 55555));

        $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 500);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 500);

        $this->redis->setOption(Redis::OPT_BACKOFF_BASE, 750);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_BASE), 750);

        $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 500);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 500);

        $this->redis->setOption(Redis::OPT_BACKOFF_CAP, 750);
        $this->assertEquals($this->redis->getOption(Redis::OPT_BACKOFF_CAP), 750);
    }

    public function testHScan() {
        if (version_compare($this->version, "2.8.0") < 0) {
            $this->markTestSkipped();
            return;
        }

        // Never get empty sets
        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);

        $this->redis->del('hash');
        $i_foo_mems = 0;

        for($i=0;$i<100;$i++) {
            if($i>3) {
                $this->redis->hset('hash', "member:$i", "value:$i");
            } else {
                $this->redis->hset('hash', "foomember:$i", "value:$i");
                $i_foo_mems++;
            }
        }

        // Scan all of them
        $it = NULL;
        while($arr_keys = $this->redis->hscan('hash', $it)) {
            $i -= count($arr_keys);
        }
        $this->assertEquals(0, $i);

        // Scan just *foomem* (should be 4)
        $it = NULL;
        while($arr_keys = $this->redis->hscan('hash', $it, '*foomember*')) {
            $i_foo_mems -= count($arr_keys);
            foreach($arr_keys as $str_mem => $str_val) {
                $this->assertTrue(strpos($str_mem, 'member')!==FALSE);
                $this->assertTrue(strpos($str_val, 'value')!==FALSE);
            }
        }
        $this->assertEquals(0, $i_foo_mems);
    }

    public function testSScan() {
        if (version_compare($this->version, "2.8.0") < 0) {
            $this->markTestSkipped();
            return;
        }

        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);

        $this->redis->del('set');
        for($i=0;$i<100;$i++) {
            $this->redis->sadd('set', "member:$i");
        }

        // Scan all of them
        $it = NULL;
        while($arr_keys = $this->redis->sscan('set', $it)) {
            $i -= count($arr_keys);
            foreach($arr_keys as $str_mem) {
                $this->assertTrue(strpos($str_mem,'member')!==FALSE);
            }
        }
        $this->assertEquals(0, $i);

        // Scan just ones with zero in them (0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
        $it = NULL;
        $i_w_zero = 0;
        while($arr_keys = $this->redis->sscan('set', $it, '*0*')) {
            $i_w_zero += count($arr_keys);
        }
        $this->assertEquals(10, $i_w_zero);
    }

    public function testZScan() {
        if (version_compare($this->version, "2.8.0") < 0) {
            $this->markTestSkipped();
            return;
        }

        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);

        $this->redis->del('zset');
        $i_tot_score = 0;
        $i_p_score = 0;
        $i_p_count = 0;
        for($i=0;$i<2000;$i++) {
            if($i<10) {
                $this->redis->zadd('zset', $i, "pmem:$i");
                $i_p_score += $i;
                $i_p_count += 1;
            } else {
                $this->redis->zadd('zset', $i, "mem:$i");
            }

            $i_tot_score += $i;
        }

        // Scan them all
        $it = NULL;
        while($arr_keys = $this->redis->zscan('zset', $it)) {
            foreach($arr_keys as $str_mem => $f_score) {
                $i_tot_score -= $f_score;
                $i--;
            }
        }

        $this->assertEquals(0, $i);
        $this->assertEquals((float)0, $i_tot_score);

        // Just scan "pmem" members
        $it = NULL;
        $i_p_score_old = $i_p_score;
        $i_p_count_old = $i_p_count;
        while($arr_keys = $this->redis->zscan('zset', $it, "*pmem*")) {
            foreach($arr_keys as $str_mem => $f_score) {
                $i_p_score -= $f_score;
                $i_p_count -= 1;
            }
        }
        $this->assertEquals((float)0, $i_p_score);
        $this->assertEquals(0, $i_p_count);

        // Turn off retrying and we should get some empty results
        $this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
        $i_skips = 0;
        $i_p_score = $i_p_score_old;
        $i_p_count = $i_p_count_old;
        $it = NULL;
        while(($arr_keys = $this->redis->zscan('zset', $it, "*pmem*")) !== FALSE) {
            if(count($arr_keys) == 0) $i_skips++;
            foreach($arr_keys as $str_mem => $f_score) {
                $i_p_score -= $f_score;
                $i_p_count -= 1;
            }
        }
        // We should still get all the keys, just with several empty results
        $this->assertTrue($i_skips > 0);
        $this->assertEquals((float)0, $i_p_score);
        $this->assertEquals(0, $i_p_count);
    }

    //
    // HyperLogLog (PF) commands
    //

    protected function createPFKey($str_key, $i_count) {
        $arr_mems = [];
        for($i=0;$i<$i_count;$i++) {
            $arr_mems[] = uniqid() . '-' . $i;
        }

        // Estimation by Redis
        $this->redis->pfadd($str_key, $i_count);
    }

    public function testPFCommands() {
        // Isn't available until 2.8.9
        if (version_compare($this->version, "2.8.9") < 0) {
            $this->markTestSkipped();
            return;
        }

        $str_uniq = uniqid();
        $arr_mems = [];

        for($i=0;$i<1000;$i++) {
            if($i%2 == 0) {
                $arr_mems[] = $str_uniq . '-' . $i;
            } else {
                $arr_mems[] = $i;
            }
        }

        // How many keys to create
        $i_keys = 10;

        // Iterate prefixing/serialization options
        foreach([Redis::SERIALIZER_NONE, Redis::SERIALIZER_PHP] as $str_ser) {
            foreach(['', 'hl-key-prefix:'] as $str_prefix) {
                $arr_keys = [];

                // Now add for each key
                for($i=0;$i<$i_keys;$i++) {
                    $str_key    = "{key}:$i";
                    $arr_keys[] = $str_key;

                    // Clean up this key
                    $this->redis->del($str_key);

                    // Add to our cardinality set, and confirm we got a valid response
                    $this->assertTrue($this->redis->pfadd($str_key, $arr_mems));

                    // Grab estimated cardinality
                    $i_card = $this->redis->pfcount($str_key);
                    $this->assertTrue(is_int($i_card));

                    // Count should be close
                    $this->assertLess(abs($i_card-count($arr_mems)), count($arr_mems) * .1);

                    // The PFCOUNT on this key should be the same as the above returned response
                    $this->assertEquals($this->redis->pfcount($str_key), $i_card);
                }

                // Clean up merge key
                $this->redis->del('pf-merge-{key}');

                // Merge the counters
                $this->assertTrue($this->redis->pfmerge('pf-merge-{key}', $arr_keys));

                // Validate our merged count
                $i_redis_card = $this->redis->pfcount('pf-merge-{key}');

                // Merged cardinality should still be roughly 1000
                $this->assertLess(abs($i_redis_card-count($arr_mems)), count($arr_mems) * .1);

                // Clean up merge key
                $this->redis->del('pf-merge-{key}');
            }
        }
    }

    //
    // GEO* command tests
    //

    protected function rawCommandArray($key, $args) {
        return call_user_func_array([$this->redis, 'rawCommand'], $args);
    }

    protected function addCities($key) {
        $this->redis->del($key);
        foreach ($this->cities as $city => $longlat) {
            $this->redis->geoadd($key, $longlat[0], $longlat[1], $city);
        }
    }

    /* GEOADD */
    public function testGeoAdd() {
        if (!$this->minVersionCheck("3.2")) {
            return $this->markTestSkipped();
        }

        $this->redis->del('geokey');

        /* Add them one at a time */
        foreach ($this->cities as $city => $longlat) {
            $this->assertEquals($this->redis->geoadd('geokey', $longlat[0], $longlat[1], $city), 1);
        }

        /* Add them again, all at once */
        $args = ['geokey'];
        foreach ($this->cities as $city => $longlat) {
            $args = array_merge($args, [$longlat[0], $longlat[1], $city]);
        }

        /* They all exist, should be nothing added */
        $this->assertEquals(call_user_func_array([$this->redis, 'geoadd'], $args), 0);
    }

    /* GEORADIUS */
    public function genericGeoRadiusTest($cmd) {
        if (!$this->minVersionCheck("3.2.0")) {
            return $this->markTestSkipped();
        }

        /* Chico */
        $city = 'Chico';
        $lng = -121.837478;
        $lat = 39.728494;

        $this->addCities('{gk}');

        /* Pre tested with redis-cli.  We're just verifying proper delivery of distance and unit */
        if ($cmd == 'georadius' || $cmd == 'georadius_ro') {
            $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi'), Array('Chico'));
            $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 30, 'mi'), Array('Gridley','Chico'));
            $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50, 'km'), Array('Gridley','Chico'));
            $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 50000, 'm'), Array('Gridley','Chico'));
            $this->assertEquals($this->redis->$cmd('{gk}', $lng, $lat, 150000, 'ft'), Array('Gridley', 'Chico'));
            $args = Array($cmd, '{gk}', $lng, $lat, 500, 'mi');

            /* Test a bad COUNT argument */
            foreach (Array(-1, 0, 'notanumber') as $count) {
                $this->assertFalse(@$this->redis->$cmd('{gk}', $lng, $lat, 10, 'mi', Array('count' => $count)));
            }
        } else {
            $this->assertEquals($this->redis->$cmd('{gk}', $city, 10, 'mi'), Array('Chico'));
            $this->assertEquals($this->redis->$cmd('{gk}', $city, 30, 'mi'), Array('Gridley','Chico'));
            $this->assertEquals($this->redis->$cmd('{gk}', $city, 50, 'km'), Array('Gridley','Chico'));
            $this->assertEquals($this->redis->$cmd('{gk}', $city, 50000, 'm'), Array('Gridley','Chico'));
            $this->assertEquals($this->redis->$cmd('{gk}', $city, 150000, 'ft'), Array('Gridley', 'Chico'));
            $args = Array($cmd, '{gk}', $city, 500, 'mi');

            /* Test a bad COUNT argument */
            foreach (Array(-1, 0, 'notanumber') as $count) {
                $this->assertFalse(@$this->redis->$cmd('{gk}', $city, 10, 'mi', Array('count' => $count)));
            }
        }

        /* Options */
        $opts = ['WITHCOORD', 'WITHDIST', 'WITHHASH'];
        $sortopts = ['', 'ASC', 'DESC'];
        $storeopts = ['', 'STORE', 'STOREDIST'];

        for ($i = 0; $i < count($opts); $i++) {
            $subopts = array_slice($opts, 0, $i);
            shuffle($subopts);

            $subargs = $args;
            foreach ($subopts as $opt) {
                $subargs[] = $opt;
            }

            /* Cannot mix STORE[DIST] with the WITH* arguments */
            $realstoreopts = count($subopts) == 0 ? $storeopts : [];

            $base_subargs = $subargs;
            $base_subopts = $subopts;

            foreach ($realstoreopts as $store_type) {

                for ($c = 0; $c < 3; $c++) {
                    $subargs = $base_subargs;
                    $subopts = $base_subopts;

                    /* Add a count if we're past first iteration */
                    if ($c > 0) {
                        $subopts['count'] = $c;
                        $subargs[] = 'count';
                        $subargs[] = $c;
                    }

                    /* Adding optional sort */
                    foreach ($sortopts as $sortopt) {
                        $realargs = $subargs;
                        $realopts = $subopts;

                        if ($sortopt) {
                            $realargs[] = $sortopt;
                            $realopts[] = $sortopt;
                        }

                        if ($store_type) {
                            $realopts[$store_type] = "{gk}-$store_type";
                            $realargs[] = $store_type;
                            $realargs[] = "{gk}-$store_type";
                        }

                        $ret1 = $this->rawCommandArray('{gk}', $realargs);
                        if ($cmd == 'georadius' || $cmd == 'georadius_ro') {
                            $ret2 = $this->redis->$cmd('{gk}', $lng, $lat, 500, 'mi', $realopts);
                        } else {
                            $ret2 = $this->redis->$cmd('{gk}', $city, 500, 'mi', $realopts);
                        }

                        $this->assertEquals($ret1, $ret2);
                    }
                }
            }
        }
    }

    public function testGeoRadius() {
        if (!$this->minVersionCheck("3.2.0")) {
            return $this->markTestSkipped();
        }

        $this->genericGeoRadiusTest('georadius');
        $this->genericGeoRadiusTest('georadius_ro');
    }

    public function testGeoRadiusByMember() {
        if (!$this->minVersionCheck("3.2.0")) {
            return $this->markTestSkipped();
        }

        $this->genericGeoRadiusTest('georadiusbymember');
        $this->genericGeoRadiusTest('georadiusbymember_ro');
    }

    public function testGeoPos() {
        if (!$this->minVersionCheck("3.2.0")) {
            return $this->markTestSkipped();
        }

        $this->addCities('gk');
        $this->assertEquals($this->redis->geopos('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', ['geopos', 'gk', 'Chico', 'Sacramento']));
        $this->assertEquals($this->redis->geopos('gk', 'Cupertino'), $this->rawCommandArray('gk', ['geopos', 'gk', 'Cupertino']));
    }

    public function testGeoHash() {
        if (!$this->minVersionCheck("3.2.0")) {
            return $this->markTestSkipped();
        }

        $this->addCities('gk');
        $this->assertEquals($this->redis->geohash('gk', 'Chico', 'Sacramento'), $this->rawCommandArray('gk', ['geohash', 'gk', 'Chico', 'Sacramento']));
        $this->assertEquals($this->redis->geohash('gk', 'Chico'), $this->rawCommandArray('gk', ['geohash', 'gk', 'Chico']));
    }

    public function testGeoDist() {
        if (!$this->minVersionCheck("3.2.0")) {
            return $this->markTestSkipped();
        }

        $this->addCities('gk');

        $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino');
        $r2 = $this->rawCommandArray('gk', ['geodist', 'gk', 'Chico', 'Cupertino']);
        $this->assertEquals(round($r1, 8), round($r2, 8));

        $r1 = $this->redis->geodist('gk', 'Chico', 'Cupertino', 'km');
        $r2 = $this->rawCommandArray('gk', ['geodist', 'gk', 'Chico', 'Cupertino', 'km']);
        $this->assertEquals(round($r1, 8), round($r2, 8));
    }

    /* Test a 'raw' command */
    public function testRawCommand() {
        $this->redis->set('mykey','some-value');
        $str_result = $this->redis->rawCommand('get', 'mykey');
        $this->assertEquals($str_result, 'some-value');

        $this->redis->del('mylist');
        $this->redis->rpush('mylist', 'A', 'B', 'C', 'D');
        $this->assertEquals($this->redis->lrange('mylist', 0, -1), ['A','B','C','D']);
    }

    /* STREAMS */

    protected function addStreamEntries($key, $count) {
        $ids = [];

        $this->redis->del($key);

        for ($i = 0; $i < $count; $i++) {
            $ids[] = $this->redis->xAdd($key, '*', ['field' => "value:$i"]);
        }

        return $ids;
    }

    protected function addStreamsAndGroups($arr_streams, $count, $arr_groups) {
        $ids = [];

        foreach ($arr_streams as $str_stream) {
            $ids[$str_stream] = $this->addStreamEntries($str_stream, $count);
            foreach ($arr_groups as $str_group => $str_id) {
                $this->redis->xGroup('CREATE', $str_stream, $str_group, $str_id);
            }
        }

        return $ids;
    }

    public function testXAdd() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        $this->redis->del('stream');
        for ($i = 0; $i < 5; $i++) {
            $id = $this->redis->xAdd("stream", '*', ['k1' => 'v1', 'k2' => 'v2']);
            $this->assertEquals($this->redis->xLen('stream'), $i+1);

            /* Redis should return <timestamp>-<sequence> */
            $bits = explode('-', $id);
            $this->assertEquals(count($bits), 2);
            $this->assertTrue(is_numeric($bits[0]));
            $this->assertTrue(is_numeric($bits[1]));
        }

        /* Test an absolute maximum length */
        for ($i = 0; $i < 100; $i++) {
            $this->redis->xAdd('stream', '*', ['k' => 'v'], 10);
        }
        $this->assertEquals($this->redis->xLen('stream'), 10);

        /* Not the greatest test but I'm unsure if approximate trimming is
         * totally deterministic, so just make sure we are able to add with
         * an approximate maxlen argument structure */
        $id = $this->redis->xAdd('stream', '*', ['k' => 'v'], 10, true);
        $this->assertEquals(count(explode('-', $id)), 2);

        /* Empty message should fail */
        $this->redis->xAdd('stream', '*', []);
    }

    protected function doXRangeTest($reverse) {
        $key = '{stream}';

        if ($reverse) {
            list($cmd,$a1,$a2) = ['xRevRange', '+', 0];
        } else {
            list($cmd,$a1,$a2) = ['xRange', 0, '+'];
        }

        $this->redis->del($key);
        for ($i = 0; $i < 3; $i++) {
            $msg = ['field' => "value:$i"];
            $id = $this->redis->xAdd($key, '*', $msg);
            $rows[$id] = $msg;
        }

        $messages = $this->redis->$cmd($key, $a1, $a2);
        $this->assertEquals(count($messages), 3);

        $i = $reverse ? 2 : 0;
        foreach ($messages as $seq => $v) {
            $this->assertEquals(count(explode('-', $seq)), 2);
            $this->assertEquals($v, ['field' => "value:$i"]);
            $i += $reverse ? -1 : 1;
        }

        /* Test COUNT option */
        for ($count = 1; $count <= 3; $count++) {
            $messages = $this->redis->$cmd($key, $a1, $a2, $count);
            $this->assertEquals(count($messages), $count);
        }
    }

    public function testXRange() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        foreach ([false, true] as $reverse) {
            foreach ($this->serializers as $serializer) {
                foreach ([NULL, 'prefix:'] as $prefix) {
                    $this->redis->setOption(Redis::OPT_PREFIX, $prefix);
                    $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);
                    $this->doXRangeTest($reverse);
                }
            }
        }
    }

    protected function testXLen() {
        if (!$this->minVersionCheck("5.0"))
            $this->markTestSkipped();

        $this->redis->del('{stream}');
        for ($i = 0; $i < 5; $i++) {
            $this->redis->xadd('{stream}', '*', ['foo' => 'bar']);
            $this->assertEquals($this->redis->xLen('{stream}'), $i+1);
        }
    }

    public function testXGroup() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        /* CREATE MKSTREAM */
        $str_key = 's:' . uniqid();
        $this->assertFalse($this->redis->xGroup('CREATE', $str_key, 'g0', 0));
        $this->assertTrue($this->redis->xGroup('CREATE', $str_key, 'g1', 0, true));

        /* XGROUP DESTROY */
        $this->assertEquals($this->redis->xGroup('DESTROY', $str_key, 'g1'), 1);

        /* Populate some entries in stream 's' */
        $this->addStreamEntries('s', 2);

        /* CREATE */
        $this->assertTrue($this->redis->xGroup('CREATE', 's', 'mygroup', '$'));
        $this->assertFalse($this->redis->xGroup('CREATE', 's', 'mygroup', 'BAD_ID'));

        /* BUSYGROUP */
        $this->redis->xGroup('CREATE', 's', 'mygroup', '$');
        $this->assertTrue(strpos($this->redis->getLastError(), 'BUSYGROUP') === 0);

        /* SETID */
        $this->assertTrue($this->redis->xGroup('SETID', 's', 'mygroup', '$'));
        $this->assertFalse($this->redis->xGroup('SETID', 's', 'mygroup', 'BAD_ID'));

        $this->assertEquals($this->redis->xGroup('DELCONSUMER', 's', 'mygroup', 'myconsumer'),0);
    }

    public function testXAck() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        for ($n = 1; $n <= 3; $n++) {
            $this->addStreamsAndGroups(['{s}'], 3, ['g1' => 0]);
            $msg = $this->redis->xReadGroup('g1', 'c1', ['{s}' => '>']);

            /* Extract IDs */
            $smsg = array_shift($msg);
            $ids = array_keys($smsg);

            /* Now ACK $n messages */
            $ids = array_slice($ids, 0, $n);
            $this->assertEquals($this->redis->xAck('{s}', 'g1', $ids), $n);
        }

        /* Verify sending no IDs is a failure */
        $this->assertFalse($this->redis->xAck('{s}', 'g1', []));
    }

    protected function doXReadTest() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        $row = ['f1' => 'v1', 'f2' => 'v2'];
        $msgdata = [
            '{stream}-1' => $row,
            '{stream}-2' => $row,
        ];

        /* Append a bit of data and populate STREAM queries */
        $this->redis->del(array_keys($msgdata));
        foreach ($msgdata as $key => $message) {
            for ($r = 0; $r < 2; $r++) {
                $id = $this->redis->xAdd($key, '*', $message);
                $qresult[$key][$id] = $message;
            }
            $qzero[$key] = 0;
            $qnew[$key] = '$';
            $keys[] = $key;
        }

        /* Everything from both streams */
        $rmsg = $this->redis->xRead($qzero);
        $this->assertEquals($rmsg, $qresult);

        /* Test COUNT option */
        for ($count = 1; $count <= 2; $count++) {
            $rmsg = $this->redis->xRead($qzero, $count);
            foreach ($keys as $key) {
                $this->assertEquals(count($rmsg[$key]), $count);
            }
        }

        /* Should be empty (no new entries) */
        $this->assertEquals(count($this->redis->xRead($qnew)),0);

        /* Test against a specific ID */
        $id = $this->redis->xAdd('{stream}-1', '*', $row);
        $new_id = $this->redis->xAdd('{stream}-1', '*', ['final' => 'row']);
        $rmsg = $this->redis->xRead(['{stream}-1' => $id]);
        $this->assertEquals(
            $this->redis->xRead(['{stream}-1' => $id]),
            ['{stream}-1' => [$new_id => ['final' => 'row']]]
        );

        /* Emtpy query should fail */
        $this->assertFalse($this->redis->xRead([]));
    }

    public function testXRead() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        foreach ($this->serializers as $serializer) {
            $this->redis->setOption(Redis::OPT_SERIALIZER, $serializer);
            $this->doXReadTest();
        }

        /* Don't need to test BLOCK multiple times */
        $m1 = round(microtime(true)*1000);
        $this->redis->xRead(['somestream' => '$'], -1, 100);
        $m2 = round(microtime(true)*1000);
        $this->assertTrue($m2 - $m1 >= 100);
    }

    protected function compareStreamIds($redis, $control) {
        foreach ($control as $stream => $ids) {
            $rcount = count($redis[$stream]);
            $lcount = count($control[$stream]);

            /* We should have the same number of messages */
            $this->assertEquals($rcount, $lcount);

            /* We should have the exact same IDs */
            foreach ($ids as $k => $id) {
                $this->assertTrue(isset($redis[$stream][$id]));
            }
        }
    }

    public function testXReadGroup() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        /* Create some streams and groups */
        $streams = ['{s}-1', '{s}-2'];
        $groups = ['g1' => 0, 'g2' => 0];

        /* I'm not totally sure why Redis behaves this way, but we have to
         * send '>' first and then send ID '0' for subsequent xReadGroup calls
         * or Redis will not return any messages.  This behavior changed from
         * redis 5.0.1 and 5.0.2 but doing it this way works for both versions. */
        $qcount = 0;
        $query1 = ['{s}-1' => '>', '{s}-2' => '>'];
        $query2 = ['{s}-1' => '0', '{s}-2' => '0'];

        $ids = $this->addStreamsAndGroups($streams, 1, $groups);

        /* Test that we get get the IDs we should */
        foreach (['g1', 'g2'] as $group) {
            foreach ($ids as $stream => $messages) {
                while ($ids[$stream]) {
                    /* Read more messages */
                    $query = !$qcount++ ? $query1 : $query2;
                    $resp = $this->redis->xReadGroup($group, 'consumer', $query);

                    /* They should match with our local control array */
                    $this->compareStreamIds($resp, $ids);

                    /* Remove a message from our control *and* XACK it in Redis */
                    $id = array_shift($ids[$stream]);
                    $this->redis->xAck($stream, $group, [$id]);
                }
            }
        }

        /* Test COUNT option */
        for ($c = 1; $c <= 3; $c++) {
            $this->addStreamsAndGroups($streams, 3, $groups);
            $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, $c);

            foreach ($resp as $stream => $smsg) {
                $this->assertEquals(count($smsg), $c);
            }
        }

        /* Test COUNT option with NULL (should be ignored) */
        $this->addStreamsAndGroups($streams, 3, $groups, NULL);
        $resp = $this->redis->xReadGroup('g1', 'consumer', $query1, NULL);
        foreach ($resp as $stream => $smsg) {
            $this->assertEquals(count($smsg), 3);
        }

        /* Finally test BLOCK with a sloppy timing test */
        $t1 = $this->mstime();
        $qnew = ['{s}-1' => '>', '{s}-2' => '>'];
        $this->redis->xReadGroup('g1', 'c1', $qnew, NULL, 100);
        $t2 = $this->mstime();
        $this->assertTrue($t2 - $t1 >= 100);

        /* Make sure passing NULL to block doesn't block */
        $t1 = $this->mstime();
        $this->redis->xReadGroup('g1', 'c1', $qnew, NULL, NULL);
        $t2 = $this->mstime();
        $this->assertTrue($t2 - $t1 < 100);

        /* Make sure passing bad values to BLOCK or COUNT immediately fails */
        $this->assertFalse(@$this->redis->xReadGroup('g1', 'c1', $qnew, -1));
        $this->assertFalse(@$this->redis->xReadGroup('g1', 'c1', $qnew, NULL, -1));
    }

    public function testXPending() {
        if (!$this->minVersionCheck("5.0")) {
            return $this->markTestSkipped();
        }

        $rows = 5;
        $this->addStreamsAndGroups(['s'], $rows, ['group' => 0]);

        $msg = $this->redis->xReadGroup('group', 'consumer', ['s' => 0]);
        $ids = array_keys($msg['s']);

        for ($n = count($ids); $n >= 0; $n--) {
            $xp = $this->redis->xPending('s', 'group');
            $this->assertEquals($xp[0], count($ids));

            /* Verify we're seeing the IDs themselves */
            for ($idx = 1; $idx <= 2; $idx++) {
                if ($xp[$idx]) {
                    $this->assertPatternMatch($xp[$idx], "/^[0-9].*-[0-9].*/");
                }
            }

            if ($ids) {
                $id = array_shift($ids);
                $this->redis->xAck('s', 'group', [$id]);
            }
        }
    }

    public function testXDel() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        for ($n = 5; $n > 0; $n--) {
            $ids = $this->addStreamEntries('s', 5);
            $todel = array_slice($ids, 0, $n);
            $this->assertEquals($this->redis->xDel('s', $todel), count($todel));
        }

        /* Empty array should fail */
        $this->assertFalse($this->redis->xDel('s', []));
    }

    public function testXTrim() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        for ($maxlen = 0; $maxlen <= 50; $maxlen += 10) {
            $this->addStreamEntries('stream', 100);
            $trimmed = $this->redis->xTrim('stream', $maxlen);
            $this->assertEquals($trimmed, 100 - $maxlen);
        }

        /* APPROX trimming isn't easily deterministic, so just make sure we
           can call it with the flag */
        $this->addStreamEntries('stream', 100);
        $this->assertFalse($this->redis->xTrim('stream', 1, true) === false);
    }

    /* XCLAIM is one of the most complicated commands, with a great deal of different options
     * The following test attempts to verify every combination of every possible option. */
    public function testXClaim() {
        if (!$this->minVersionCheck("5.0"))
            return $this->markTestSkipped();

        foreach ([0, 100] as $min_idle_time) {
            foreach ([false, true] as $justid) {
                foreach ([0, 10] as $retrycount) {
                    /* We need to test not passing TIME/IDLE as well as passing either */
                    if ($min_idle_time == 0) {
                        $topts = [[], ['IDLE', 1000000], ['TIME', time() * 1000]];
                    } else {
                        $topts = [NULL];
                    }

                    foreach ($topts as $tinfo) {
                        if ($tinfo) {
                            list($ttype, $tvalue) = $tinfo;
                        } else {
                            $ttype = NULL; $tvalue = NULL;
                        }

                        /* Add some messages and create a group */
                        $this->addStreamsAndGroups(['s'], 10, ['group1' => 0]);

                        /* Create a second stream we can FORCE ownership on */
                        $fids = $this->addStreamsAndGroups(['f'], 10, ['group1' => 0]);
                        $fids = $fids['f'];

                        /* Have consumer 'Mike' read the messages */
                        $oids = $this->redis->xReadGroup('group1', 'Mike', ['s' => '>']);
                        $oids = array_keys($oids['s']); /* We're only dealing with stream 's' */

                        /* Construct our options array */
                        $opts = [];
                        if ($justid) $opts[] = 'JUSTID';
                        if ($retrycount) $opts['RETRYCOUNT'] = $retrycount;
                        if ($tvalue !== NULL) $opts[$ttype] = $tvalue;

                        /* Now have pavlo XCLAIM them */
                        $cids = $this->redis->xClaim('s', 'group1', 'Pavlo', $min_idle_time, $oids, $opts);
                        if (!$justid) $cids = array_keys($cids);

                        if ($min_idle_time == 0) {
                            $this->assertEquals($cids, $oids);

                            /* Append the FORCE option to our second stream where we have not already
                             * assigned to a PEL group */
                            $opts[] = 'FORCE';
                            $freturn = $this->redis->xClaim('f', 'group1', 'Test', 0, $fids, $opts);
                            if (!$justid) $freturn = array_keys($freturn);
                            $this->assertEquals($freturn, $fids);

                            if ($retrycount || $tvalue !== NULL) {
                                $pending = $this->redis->xPending('s', 'group1', 0, '+', 1, 'Pavlo');

                                if ($retrycount) {
                                    $this->assertEquals($pending[0][3], $retrycount);
                                }
                                if ($tvalue !== NULL) {
                                    if ($ttype == 'IDLE') {
                                        /* If testing IDLE the value must be >= what we set */
                                        $this->assertTrue($pending[0][2] >= $tvalue);
                                    } else {
                                        /* Timing tests are notoriously irritating but I don't see
                                         * how we'll get >= 20,000 ms between XCLAIM and XPENDING no
                                         * matter how slow the machine/VM running the tests is */
                                        $this->assertTrue($pending[0][2] <= 20000);
                                    }
                                }
                            }
                        } else {
                            /* We're verifying that we get no messages when we've set 100 seconds
                             * as our idle time, which should match nothing */
                            $this->assertEquals($cids, []);
                        }
                    }
                }
            }
        }
    }

    public function testXInfo()
    {
        if (!$this->minVersionCheck("5.0")) {
            return $this->markTestSkipped();
        }
        /* Create some streams and groups */
        $stream = 's';
        $groups = ['g1' => 0, 'g2' => 0];
        $this->addStreamsAndGroups([$stream], 1, $groups);

        $info = $this->redis->xInfo('GROUPS', $stream);
        $this->assertTrue(is_array($info));
        $this->assertEquals(count($info), count($groups));
        foreach ($info as $group) {
            $this->assertTrue(array_key_exists('name', $group));
            $this->assertTrue(array_key_exists($group['name'], $groups));
        }

        $info = $this->redis->xInfo('STREAM', $stream);
        $this->assertTrue(is_array($info));
        $this->assertTrue(array_key_exists('groups', $info));
        $this->assertEquals($info['groups'], count($groups));
        foreach (['first-entry', 'last-entry'] as $key) {
            $this->assertTrue(array_key_exists($key, $info));
            $this->assertTrue(is_array($info[$key]));
        }

        /* XINFO STREAM FULL [COUNT N] Requires >= 6.0.0 */
        if (!$this->minVersionCheck("6.0"))
            return;

        /* Add some items to the stream so we can test COUNT */
        for ($i = 0; $i < 5; $i++) {
            $this->redis->xAdd($stream, '*', ['foo' => 'bar']);
        }

        $info = $this->redis->xInfo('STREAM', $stream, 'full');
        $this->assertTrue(isset($info['groups']));

        for ($count = 1; $count < 5; $count++) {
            $info = $this->redis->xInfo('STREAM', $stream, 'full', $count);
            $n = isset($info['entries']) ? count($info['entries']) : 0;
            $this->assertEquals($n, $count);
        }

        /* Count <= 0 should be ignored */
        foreach ([-1, 0] as $count) {
            $info = $this->redis->xInfo('STREAM', $stream, 'full', 0);
            $n = isset($info['entries']) ? count($info['entries']) : 0;
            $this->assertEquals($n, $this->redis->xLen($stream));
        }
    }

    /* Regression test for issue-1831 (XINFO STREAM on an empty stream) */
    public function testXInfoEmptyStream() {
        /* Configure an empty stream */
        $this->redis->del('s');
        $this->redis->xAdd('s', '*', ['foo' => 'bar']);
        $this->redis->xTrim('s', 0);

        $arr_info = $this->redis->xInfo('STREAM', 's');

        $this->assertTrue(is_array($arr_info));
        $this->assertEquals(0, $arr_info['length']);
        $this->assertEquals(NULL, $arr_info['first-entry']);
        $this->assertEquals(NULL, $arr_info['last-entry']);
    }

    public function testInvalidAuthArgs() {
        $obj_new = $this->newInstance();

        $arr_args = [
            [],
            [NULL, NULL],
            ['foo', 'bar', 'baz'],
            ['a','b','c','d'],
            ['a','b','c'],
            [['a','b'], 'a'],
            [['a','b','c']],
            [[NULL, 'pass']],
            [[NULL, NULL]],
        ];

        foreach ($arr_args as $arr_arg) {
            try {
                if (is_array($arr_arg)) {
                    @call_user_func_array([$obj_new, 'auth'], $arr_arg);
                }
            } catch (Exception $ex) {
                unset($ex); /* Suppress intellisense warning */
            } catch (ArgumentCountError $ex) {
                unset($ex); /* Suppress intellisense warning */
            }
        }
    }

    public function testAcl() {
        if ( ! $this->minVersionCheck("6.0"))
            return $this->markTestSkipped();

        /* ACL USERS/SETUSER */
        $this->assertTrue(in_array('default', $this->redis->acl('USERS')));
        $this->assertTrue($this->redis->acl('SETUSER', 'admin', 'on', '>admin', '+@all'));
        $this->assertTrue($this->redis->acl('SETUSER', 'noperm', 'on', '>noperm', '-@all'));
        $this->assertInArray('default', $this->redis->acl('USERS'));

        /* Verify ACL GETUSER has the correct hash and is in 'nice' format */
        $arr_admin = $this->redis->acl('GETUSER', 'admin');
        $this->assertInArray(hash('sha256', 'admin'), $arr_admin['passwords']);

        /* Now nuke our 'admin' user and make sure it went away */
        $this->assertTrue($this->redis->acl('DELUSER', 'admin'));
        $this->assertTrue(!in_array('admin', $this->redis->acl('USERS')));

        /* Try to log in with a bad username/password */
        $this->assertThrowsMatch($this->redis,
            function($o) { $o->auth(['1337haxx00r', 'lolwut']); }, '/^WRONGPASS.*$/');

        /* We attempted a bad login.  We should have an ACL log entry */
        $arr_log = $this->redis->acl('log');
        if (! $arr_log || !is_array($arr_log)) {
            $this->assertTrue(false);
            return;
        }

        /* Make sure our ACL LOG entries are nice for the user */
        $arr_entry = array_shift($arr_log);
        $this->assertArrayKey($arr_entry, 'age-seconds', 'is_numeric');
        $this->assertArrayKey($arr_entry, 'count', 'is_int');

        /* ACL CAT */
        $cats = $this->redis->acl('CAT');
        foreach (['read', 'write', 'slow'] as $cat) {
            $this->assertInArray($cat, $cats);
        }

        /* ACL CAT <string> */
        $cats = $this->redis->acl('CAT', 'string');
        foreach (['get', 'set', 'setnx'] as $cat) {
            $this->assertInArray($cat, $cats);
        }

        /* ctype_xdigit even if PHP doesn't have it */
        $ctype_xdigit = function($v) {
            if (function_exists('ctype_xdigit')) {
                return ctype_xdigit($v);
            } else {
                return strspn(strtoupper($v), '0123456789ABCDEF') == strlen($v);
            }
        };

        /* ACL GENPASS/ACL GENPASS <bits> */
        $this->assertValidate($this->redis->acl('GENPASS'), $ctype_xdigit);
        $this->assertValidate($this->redis->acl('GENPASS', 1024), $ctype_xdigit);

        /* ACL WHOAMI */
        $this->assertValidate($this->redis->acl('WHOAMI'), 'strlen');

        /* Finally make sure AUTH errors throw an exception */
        $r2 = $this->newInstance(true);

        /* Test NOPERM exception */
        $this->assertTrue($r2->auth(['noperm', 'noperm']));
        $this->assertThrowsMatch($r2, function($r) { $r->set('foo', 'bar'); }, '/^NOPERM.*$/');
    }

    /* If we detect a unix socket make sure we can connect to it in a variety of ways */
    public function testUnixSocket() {
        if ( ! file_exists("/tmp/redis.sock")) {
            return $this->markTestSkipped();
        }

        $arr_sock_tests = [
            ["/tmp/redis.sock"],
            ["/tmp/redis.sock", null],
            ["/tmp/redis.sock", 0],
            ["/tmp/redis.sock", -1],
        ];

        try {
            foreach ($arr_sock_tests as $arr_args) {
                $obj_r = new Redis();

                if (count($arr_args) == 2) {
                    @$obj_r->connect($arr_args[0], $arr_args[1]);
                } else {
                    @$obj_r->connect($arr_args[0]);
                }
                if ($this->getAuth()) {
                    $this->assertTrue($obj_r->auth($this->getAuth()));
                }
                $this->assertTrue($obj_r->ping());
            }
        } catch (Exception $ex) {
            $this->assertTrue(false);
        }
    }

    /* Test high ports if we detect Redis running there */
    public function testHighPorts() {
        $arr_ports = [32767, 32768, 32769];
        $arr_test_ports = [];

        foreach ($arr_ports as $port) {
            if (is_resource(@fsockopen('localhost', $port))) {
                $arr_test_ports[] = $port;
            }
        }

        if ( ! $arr_test_ports) {
            return $this->markTestSkipped();
        }

        foreach ($arr_test_ports as $port) {
            $obj_r = new Redis();
            try {
                @$obj_r->connect('localhost', $port);
                if ($this->getAuth()) {
                    $this->assertTrue($obj_r->auth($this->getAuth()));
                }
                $this->assertTrue($obj_r->ping());
            } catch(Exception $ex) {
                $this->assertTrue(false);
            }
        }
    }

    public function testSession_savedToRedis()
    {
        $this->setSessionHandler();

        $sessionId = $this->generateSessionId();
        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);

        $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId));
        $this->assertTrue($sessionSuccessful);
    }

    public function testSession_lockKeyCorrect()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();

        $this->startSessionProcess($sessionId, 5, true);

        $maxwait = (ini_get('redis.session.lock_wait_time') *
                    ini_get('redis.session.lock_retries') /
                    1000000.00);

        $exist = $this->waitForSessionLockKey($sessionId, $maxwait + 1);
        $this->assertTrue($exist);
    }

    public function testSession_lockingDisabledByDefault()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 5, true, 300, false);
        usleep(100000);

        $start = microtime(true);
        $sessionSuccessful = $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, false);
        $end = microtime(true);
        $elapsedTime = $end - $start;

        $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
        $this->assertTrue($elapsedTime < 1);
        $this->assertTrue($sessionSuccessful);
    }

    public function testSession_lockReleasedOnClose()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 1, true);

        /* Wait for a key to actually exist */
        if ( ! $this->waitForSessionLockKey($sessionId, 1)) {
            $this->assertFalse(true);
            return;
        }

        /* Wait long enough for our background process to exit */
        usleep(1100000);

        /* Key should have been deleted */
        $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
    }

    public function testSession_lock_ttlMaxExecutionTime()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 10, true, 2);
        usleep(100000);

        $start = microtime(true);
        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
        $end = microtime(true);
        $elapsedTime = $end - $start;

        $this->assertTrue($elapsedTime < 3);
        $this->assertTrue($sessionSuccessful);
    }

    public function testSession_lock_ttlLockExpire()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 10, true, 300, true, null, -1, 2);
        usleep(100000);

        $start = microtime(true);
        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
        $end = microtime(true);
        $elapsedTime = $end - $start;

        $this->assertTrue($elapsedTime < 3);
        $this->assertTrue($sessionSuccessful);
    }

    public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 2, true, 300, true, null, -1, 1, 'firstProcess');
        usleep(1500000); // 1.5 sec
        $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 10, 'secondProcess');
        sleep(1);

        $this->assertTrue($writeSuccessful);
        $this->assertEquals('secondProcess', $this->getSessionData($sessionId));
    }

    public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $writeSuccessful = $this->startSessionProcess($sessionId, 2, false, 300, true, null, -1, 1, 'firstProcess');

        $this->assertFalse($writeSuccessful);
        $this->assertTrue('firstProcess' !== $this->getSessionData($sessionId));
    }

    public function testSession_correctLockRetryCount() {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();

        /* Start another process and wait until it has the lock */
        $this->startSessionProcess($sessionId, 10, true);
        if ( ! $this->waitForSessionLockKey($sessionId, 2)) {
            $this->assertTrue(false);
            return;
        }

        $t1 = microtime(true);
        $ok = $this->startSessionProcess($sessionId, 0, false, 10, true, 100000, 10);
        if ( ! $this->assertFalse($ok)) return;
        $t2 = microtime(true);

        $this->assertTrue($t2 - $t1 >= 1 && $t2 - $t1 <= 3);
    }

    public function testSession_defaultLockRetryCount()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 10, true);

        $keyname = $this->sessionPrefix . $sessionId . '_LOCK';
        $begin = microtime(true);

        if ( ! $this->waitForSessionLockKey($sessionId, 3)) {
            $this->assertTrue(false);
            return;
        }

        $start = microtime(true);
        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 200000, 0);
        $end = microtime(true);
        $elapsedTime = $end - $start;

        $this->assertTrue($elapsedTime > 2 && $elapsedTime < 3);
        $this->assertFalse($sessionSuccessful);
    }

    public function testSession_noUnlockOfOtherProcess()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();

        $t1 = microtime(true);

        /* 1.  Start a background process, and wait until we are certain
         *     the lock was attained. */
        $nsec = 3;
        $this->startSessionProcess($sessionId, $nsec, true, $nsec);
        if ( ! $this->waitForSessionLockKey($sessionId, 1)) {
            $this->assertFalse(true);
            return;
        }

        /* 2.  Attempt to lock the same session.  This should force us to
         *     wait until the first lock is released. */
        $t2 = microtime(true);
        $ok = $this->startSessionProcess($sessionId, 0, false);
        $t3 = microtime(true);

        /* 3.  Verify that we did in fact have to wait for this lock */
        $this->assertTrue($ok);
        $this->assertTrue($t3 - $t2 >= $nsec - ($t2 - $t1));
    }

    public function testSession_lockWaitTime()
    {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 1, true, 300);
        usleep(100000);

        $start = microtime(true);
        $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, 3000000);
        $end = microtime(true);
        $elapsedTime = $end - $start;

        $this->assertTrue($elapsedTime > 2.5);
        $this->assertTrue($elapsedTime < 3.5);
        $this->assertTrue($sessionSuccessful);
    }

    public function testMultipleConnect() {
        $host = $this->redis->GetHost();
        $port = $this->redis->GetPort();

        for($i = 0; $i < 5; $i++) {
            $this->redis->connect($host, $port);
            if ($this->getAuth()) {
                $this->assertTrue($this->redis->auth($this->getAuth()));
            }
            $this->assertTrue($this->redis->ping());
        }
    }

    public function testConnectException() {
        $host = 'github.com';
        if (gethostbyname($host) === $host) {
            return $this->markTestSkipped('online test');
        }
        $redis = new Redis();
        try {
            $redis->connect($host, 6379, 0.01);
        }  catch (Exception $e) {
            $this->assertTrue(strpos($e, "timed out") !== false);
        }
    }

    public function testTlsConnect()
    {
        if (($fp = @fsockopen($this->getHost(), 6378)) == NULL)
            return $this->markTestSkipped();

        fclose($fp);

        foreach (['localhost' => true, '127.0.0.1' => false] as $host => $verify) {
            $redis = new Redis();
            $this->assertTrue($redis->connect('tls://' . $host, 6378, 0, null, 0, 0, [
                'stream' => ['verify_peer_name' => $verify, 'verify_peer' => false]
            ]));
        }
    }

    public  function testSession_regenerateSessionId_noLock_noDestroy() {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');

        $newSessionId = $this->regenerateSessionId($sessionId);

        $this->assertTrue($newSessionId !== $sessionId);
        $this->assertEquals('bar', $this->getSessionData($newSessionId));
    }

    public  function testSession_regenerateSessionId_noLock_withDestroy() {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');

        $newSessionId = $this->regenerateSessionId($sessionId, false, true);

        $this->assertTrue($newSessionId !== $sessionId);
        $this->assertEquals('bar', $this->getSessionData($newSessionId));
    }

    public  function testSession_regenerateSessionId_withLock_noDestroy() {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');

        $newSessionId = $this->regenerateSessionId($sessionId, true);

        $this->assertTrue($newSessionId !== $sessionId);
        $this->assertEquals('bar', $this->getSessionData($newSessionId));
    }

    public  function testSession_regenerateSessionId_withLock_withDestroy() {
        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');

        $newSessionId = $this->regenerateSessionId($sessionId, true, true);

        $this->assertTrue($newSessionId !== $sessionId);
        $this->assertEquals('bar', $this->getSessionData($newSessionId));
    }

    public  function testSession_regenerateSessionId_noLock_noDestroy_withProxy() {
        if (!interface_exists('SessionHandlerInterface')) {
            $this->markTestSkipped('session handler interface not available in PHP < 5.4');
        }

        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');

        $newSessionId = $this->regenerateSessionId($sessionId, false, false, true);

        $this->assertTrue($newSessionId !== $sessionId);
        $this->assertEquals('bar', $this->getSessionData($newSessionId));
    }

    public  function testSession_regenerateSessionId_noLock_withDestroy_withProxy() {
        if (!interface_exists('SessionHandlerInterface')) {
            $this->markTestSkipped('session handler interface not available in PHP < 5.4');
        }

        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');

        $newSessionId = $this->regenerateSessionId($sessionId, false, true, true);

        $this->assertTrue($newSessionId !== $sessionId);
        $this->assertEquals('bar', $this->getSessionData($newSessionId));
    }

    public  function testSession_regenerateSessionId_withLock_noDestroy_withProxy() {
        if (!interface_exists('SessionHandlerInterface')) {
            $this->markTestSkipped('session handler interface not available in PHP < 5.4');
        }

        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');

        $newSessionId = $this->regenerateSessionId($sessionId, true, false, true);

        $this->assertTrue($newSessionId !== $sessionId);
        $this->assertEquals('bar', $this->getSessionData($newSessionId));
    }

    public  function testSession_regenerateSessionId_withLock_withDestroy_withProxy() {
        if (!interface_exists('SessionHandlerInterface')) {
            $this->markTestSkipped('session handler interface not available in PHP < 5.4');
        }

        $this->setSessionHandler();
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');

        $newSessionId = $this->regenerateSessionId($sessionId, true, true, true);

        $this->assertTrue($newSessionId !== $sessionId);
        $this->assertEquals('bar', $this->getSessionData($newSessionId));
    }

    public function testSession_ttl_equalsToSessionLifetime()
    {
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600);
        $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId);

        $this->assertEquals(600, $ttl);
    }

    public function testSession_ttl_resetOnWrite()
    {
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600);
        $this->redis->expire($this->sessionPrefix . $sessionId, 9999);
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600);
        $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId);

        $this->assertEquals(600, $ttl);
    }

    public function testSession_ttl_resetOnRead()
    {
        $sessionId = $this->generateSessionId();
        $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 0, 'test', 600);
        $this->redis->expire($this->sessionPrefix . $sessionId, 9999);
        $this->getSessionData($sessionId, 600);
        $ttl = $this->redis->ttl($this->sessionPrefix . $sessionId);

        $this->assertEquals(600, $ttl);
    }

    private function setSessionHandler()
    {
        $host = $this->getHost() ?: 'localhost';

        @ini_set('session.save_handler', 'redis');
        @ini_set('session.save_path', 'tcp://' . $host . ':6379');
    }

    /**
     * @return string
     */
    private function generateSessionId()
    {
        if (function_exists('session_create_id')) {
            return session_create_id();
        } else if (function_exists('random_bytes')) {
            return bin2hex(random_bytes(8));
        } else if (function_exists('openssl_random_pseudo_bytes')) {
            return bin2hex(openssl_random_pseudo_bytes(8));
        } else {
            return uniqid();
        }
    }

    /**
     * @param string $sessionId
     * @param int    $sleepTime
     * @param bool   $background
     * @param int    $maxExecutionTime
     * @param bool   $locking_enabled
     * @param int    $lock_wait_time
     * @param int    $lock_retries
     * @param int    $lock_expires
     * @param string $sessionData
     *
     * @param int    $sessionLifetime
     *
     * @return bool
     * @throws Exception
     */
    private function startSessionProcess($sessionId, $sleepTime, $background, $maxExecutionTime = 300,
                                         $locking_enabled = true, $lock_wait_time = null, $lock_retries = -1,
                                         $lock_expires = 0, $sessionData = '', $sessionLifetime = 1440)
    {
        if (substr(php_uname(), 0, 7) == "Windows"){
            $this->markTestSkipped();
            return true;
        } else {
            $commandParameters = [
                $this->getFullHostPath(), $this->sessionSaveHandler, $sessionId,
                $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires,
                $sessionData, $sessionLifetime
            ];

            if ($locking_enabled) {
                $commandParameters[] = '1';

                if ($lock_wait_time != null) {
                    $commandParameters[] = $lock_wait_time;
                }
            }
            $commandParameters = array_map('escapeshellarg', $commandParameters);

            $command = self::getPhpCommand('startSession.php') . implode(' ', $commandParameters);
            $command .= $background ? ' 2>/dev/null > /dev/null &' : ' 2>&1';
            exec($command, $output);
            return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')) ? true : false;
        }
    }

    /**
     * @param string $session_id
     * @param string $max_wait_sec
     *
     * Sometimes we want to block until a session lock has been detected
     * This is better and faster than arbitrarily sleeping.  If we don't
     * detect the session key within the specified maximum number of
     * seconds, the function returns failure.
     *
     * @return bool
     */
    private function waitForSessionLockKey($session_id, $max_wait_sec) {
        $now = microtime(true);
        $key = $this->sessionPrefix . $session_id . '_LOCK';

        do {
            usleep(10000);
            $exists = $this->redis->exists($key);
        } while (!$exists && microtime(true) <= $now + $max_wait_sec);

        return $exists || $this->redis->exists($key);
    }

    /**
     * @param string $str_search pattern to look for in ps
     * @param int    $timeout    Maximum amount of time to wait
     *
     * Small helper function to wait until we no longer detect a running process.
     * This is an attempt to fix timing related false failures on session tests
     * when running in CI.
     */
    function waitForProcess($str_search, $timeout = 0.0) {
        $st = microtime(true);

        do {
            $str_procs = shell_exec("ps aux|grep $str_search|grep -v grep");
            $arr_procs = array_filter(explode("\n", $str_procs));
            if (count($arr_procs) == 0)
                return true;

            usleep(10000);
            $elapsed = microtime(true) - $st;
        } while ($timeout < 0 || $elapsed < $timeout);

        return false;
    }

    /**
     * @param string $sessionId
     * @param int    $sessionLifetime
     *
     * @return string
     */
    private function getSessionData($sessionId, $sessionLifetime = 1440)
    {
        $command = self::getPhpCommand('getSessionData.php') . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . escapeshellarg($sessionId) . ' ' . escapeshellarg($sessionLifetime);
        exec($command, $output);

        return $output[0];
    }

    /**
     * @param string $sessionId
     * @param bool   $locking
     * @param bool   $destroyPrevious
     * @param bool   $sessionProxy
     *
     * @return string
     */
    private function regenerateSessionId($sessionId, $locking = false, $destroyPrevious = false, $sessionProxy = false)
    {
	$args = array_map('escapeshellarg', [$sessionId, $locking, $destroyPrevious, $sessionProxy]);

        $command = self::getPhpCommand('regenerateSessionId.php') . escapeshellarg($this->getFullHostPath()) . ' ' . $this->sessionSaveHandler . ' ' . implode(' ', $args);

        exec($command, $output);

        return $output[0];
    }

    /**
     * Return command to launch PHP with built extension enabled
     * taking care of environment (TEST_PHP_EXECUTABLE and TEST_PHP_ARGS)
     *
     * @param string $script
     *
     * @return string
     */
    private static function getPhpCommand($script)
    {
        static $cmd = NULL;

        if (!$cmd) {
            $cmd  = (getenv('TEST_PHP_EXECUTABLE') ?: PHP_BINARY);

            if ($test_args = getenv('TEST_PHP_ARGS')) {
                $cmd .= ' ';
                $cmd .= $test_args;
            } else {
                /* Only append specific extension directives if PHP hasn't been compiled with what we need statically */
                $modules   = shell_exec("$cmd --no-php-ini -m");

                /* Determine if we need to specifically add extensions */
                $arr_extensions = [];

                /* If any are needed add them to the command */
                if ($arr_extensions) {
                    $cmd .= ' --no-php-ini';
                    foreach ($arr_extensions as $str_extension) {
                        /* We want to use the locally built redis extension */
                        if ($str_extension == 'redis') {
                            $str_extension = dirname(__DIR__) . '/modules/redis';
                        }

                        $cmd .= " --define extension=$str_extension.so";
                    }
                }
            }
        }

        return $cmd . ' ' . __DIR__ . '/' . $script . ' ';
    }
}
?>
PK�[�\�m��P
P
RedisSentinelTest.phpnu�[���<?php defined('PHPREDIS_TESTRUN') or die("Use TestRedis.php to run tests!\n");

require_once(dirname($_SERVER['PHP_SELF'])."/TestSuite.php");

class Redis_Sentinel_Test extends TestSuite
{
    const NAME = 'mymaster';

    /**
     * @var RedisSentinel
     */
    public $sentinel;

    /**
     * Common fields
     */
    protected $fields = [
        'name',
        'ip',
        'port',
        'runid',
        'flags',
        'link-pending-commands',
        'link-refcount',
        'last-ping-sent',
        'last-ok-ping-reply',
        'last-ping-reply',
        'down-after-milliseconds',
    ];

    protected function newInstance()
    {
        return new RedisSentinel($this->getHost());
    }

    public function setUp()
    {
        $this->sentinel = $this->newInstance();
    }

    public function testCkquorum()
    {
        $this->assertTrue(is_bool($this->sentinel->ckquorum(self::NAME)));
    }

    public function testFailover()
    {
        $this->assertFalse($this->sentinel->failover(self::NAME));
    }

    public function testFlushconfig()
    {
        $this->assertTrue($this->sentinel->flushconfig());
    }

    public function testGetMasterAddrByName()
    {
        $result = $this->sentinel->getMasterAddrByName(self::NAME);
        $this->assertTrue(is_array($result));
        $this->assertEquals(2, count($result));
    }

    protected function checkFields(array $fields)
    {
        foreach ($this->fields as $k) {
            $this->assertTrue(array_key_exists($k, $fields));
        }
    }

    public function testMaster()
    {
        $result = $this->sentinel->master(self::NAME);
        $this->assertTrue(is_array($result));
        $this->checkFields($result);
    }

    public function testMasters()
    {
        $result = $this->sentinel->masters();
        $this->assertTrue(is_array($result));
        foreach ($result as $master) {
            $this->checkFields($master);
        }
    }

    public function testPing()
    {
        $this->assertTrue($this->sentinel->ping());
    }

    public function testReset()
    {
        $this->assertFalse($this->sentinel->reset('*'));
    }

    public function testSentinels()
    {
        $result = $this->sentinel->sentinels(self::NAME);
        $this->assertTrue(is_array($result));
        foreach ($result as $sentinel) {
            $this->checkFields($sentinel);
        }
    }

    public function testSlaves()
    {
        $result = $this->sentinel->slaves(self::NAME);
        $this->assertTrue(is_array($result));
        foreach ($result as $slave) {
            $this->checkFields($slave);
        }
    }
}
PK��\0��#��&gmagickdraw-033_setClipPath_basic.phptnu�[���PK��\y��*\\?'gmagickpixel-005-setcolorvaluequantum_getcolorvaluequantum.phptnu�[���PK��\e��{UU,�gmagick-041-set_getimageinterlacescheme.phptnu�[���PK��\�X�  #�	gmagick-015-cyclecolormapimage.phptnu�[���PK��\�?Jgmagick-067-minifyimage.phptnu�[���PK��\�j�h��.xgmagickdraw-027-setantialias_getantialias.phptnu�[���PK��\����@@'�
gmagick-042-set_getimageiterations.phptnu�[���PK��\ؠ�E~~=Sgmagickdraw_040_pathCurveToQuadraticBezierAbsolute_basic.phptnu�[���PK��\)�%%!>gmagick-060-haspreviousimage.phptnu�[���PK��\������#�gmagick-102-unsharpenmaskimage.phptnu�[���PK��\�rKdEE!�gmagick-092-getimagegeometry.phptnu�[���PK��\oU�h��}gmagick-105-readimageblob.phptnu�[���PK��\�?�FF6�gmagickdraw-012-setstrokeopacity_getstrokeopacity.phptnu�[���PK��\}��00">gmagick-037-set_getimagegamma.phptnu�[���PK��\,�`��(� gmagick-027-set_getimagebordercolor.phptnu�[���PK��\>~22#gmagick-075-quantizeimages.phptnu�[���PK��\L̚��$gmagick-083-solarizeimage.phptnu�[���PK��\(A�;;(�%gmagickdraw_045_setFontFamily_basic.phptnu�[���PK��\�!�"�+gmagick-126_negateImage_basic.phptnu�[���PK��\����pp'�-gmagick-044-set_getimageredprimary.phptnu�[���PK��\�+"�&&�/gmagick-013-commentimage.phptnu�[���PK��\pn>>.1gmagickdraw-010-setfontstyle_getfontstyle.phptnu�[���PK��\˺�y** �2gmagick-070-motionblurimage.phptnu�[���PK��\,��@@24gmagick-090-setimage.phptnu�[���PK��\R�*�5gmagick-093-flattenimages.phptnu�[���PK��\6,��mm!7gmagickdraw-025-affine.phptnu�[���PK��\)jN	00.�8gmagick-025-set_and_getimageckgroundcolor.phptnu�[���PK��\���g;gmagick-021-equalizeimage.phptnu�[���PK��\��Q--+�<gmagickdraw_042_setTextAntialias_basic.phptnu�[���PK��\%q�3tt%NBgmagick-038-setimagegreenprimary.phptnu�[���PK��\��t�``'Dgmagickdraw_035_setClipUnits_basic.phptnu�[���PK��\�۬�//�Igmagick-074-quantizeimage.phptnu�[���PK��\���!��KKgmagick-097-sampleimage.phptnu�[���PK��\����LL0_Lgmagickdraw-030-setmiterlimit_getmiterlimit.phptnu�[���PK��\��fNgmagick-023-gammaimage.phptnu�[���PK��\�om��
eObug63677.phptnu�[���PK��\v��Tgmagick-062-labelimage.phptnu�[���PK��\�i� ���Ugmagickdraw-021-rectangle.phptnu�[���PK��\�zZ,22�Wgmagick-073-profileimage.phptnu�[���PK��\���eYgmagick-086-swirlimage.phptnu�[���PK��\pQ���$�Zgmagickdraw_039_pathStart_basic.phptnu�[���PK��\/���agmagick-017-destroy.phptnu�[���PK��\�P??2[bgmagickdraw-007-setfillopacity_getfillopacity.phptnu�[���PK��\٣;}nn�cgmagick-011-charcoalimage.phptnu�[���PK��\�i�ˆ� �egmagick-114_convolveImage_6.phptnu�[���PK��\�`0��&�hgmagickdraw_037_setFillRule_basic.phptnu�[���PK��\!��upgmagick-016-despeckleimage.phptnu�[���PK��\>��DD'�qgmagick-029-set_getimagecolorspace.phptnu�[���PK��\m�x�662ysgmagickdraw-014-setstrokewidth_getstrokewidth.phptnu�[���PK��\qE**"ugmagick-066-medianfilterimage.phptnu�[���PK��\��*���#�vgmagickdraw-023-roundrectangle.phptnu�[���PK��\�A����xgmagickdraw-004-arc.phptnu�[���PK��\�#�X''!�zgmagick-049-set_getimagetype.phptnu�[���PK��\���D|gmagick-061-implodeimage.phptnu�[���PK��\�(W��r}gmagickdraw-005-bezier.phptnu�[���PK��\�(���gmagick-080-scaleimage.phptnu�[���PK��\K�{��#�gmagick-096-setinterlacescheme.phptnu�[���PK��\��d**?�gmagick-005-draw.phptnu�[���PK��\�Y	))"��gmagick-040-getimagehistogram.phptnu�[���PK��\��k�)�gmagick-039-getimageheight.phptnu�[���PK��\bi
\����gmagick-099-appendimages.phptnu�[���PK��\�a����2ȉgmagickdraw-013-setstrokecolor_getstrokecolor.phptnu�[���PK��\�+iB&�gmagickdraw_036_pushPattern_basic.phptnu�[���PK��\������,v�gmagick-027-set_getimagebordercolor_q16.phptnu�[���PK��\+��ϔgmagick-091-getimage.phptnu�[���PK��\��v��'�gmagick-122_unsharpMaskImage_basic.phptnu�[���PK��\(}t�  �gmagick-053-getpackagename.phptnu�[���PK��\P(P�DD p�gmagick-035-set_getfilename.phptnu�[���PK��\62�՜��gmagick-065-mapimage.phptnu�[���PK��\�2K��gmagick-019-embossimage.phptnu�[���PK��\I.eN��"H�gmagick-003-resize_variations.phptnu�[���PK��\#�	�OO)H�gmagick-028-set_getimagechanneldepth.phptnu�[���PK��\�0�Q�gmagick-100-coalesceimages.phptnu�[���PK��\��Μ�Q�gmagick-010-borderimage.phptnu�[���PK��\4jȴ��9�gmagick-101-sharpenimage.phptnu�[���PK��\���**,T�gmagickdraw_044_setTextUnderColor_basic.phptnu�[���PK��\�[-0'ګgmagick-103-setgetimagecompression.phptnu�[���PK��\����SS%5�gmagick-036-set_getimagefilename.phptnu�[���PK��\4�-���.ݮgmagickdraw-006-setfillcolor_getfillcolor.phptnu�[���PK��\~:}�gmagick-076-queryfonts.phptnu�[���PK��\ԱsFF4|�gmagickdraw-016-settextencoding_gettextencoding.phptnu�[���PK��\9M�`&�gmagick-084-spreadimage.phptnu�[���PK��\	���YY$��gmagick-043-set_getimageprofile.phptnu�[���PK��\�v�dd87�gmagickdraw-015-settextdecoration_gettextdecoration.phptnu�[���PK��\�ƂE00$�gmagick-034-set_getimagedispose.phptnu�[���PK��\�[��
��skipif.incnu�[���PK��\H;�V��gmagick-014-current.phptnu�[���PK��\<"�NEE)�gmagick-024-getcopyright_getfilename.phptnu�[���PK��\�B�[��gmagick-072-oilpaintimage.phptnu�[���PK��\�6�h88#�gmagick-071-next_previousimage.phptnu�[���PK��\Wr�����gmagick-113_compareImages.phptnu�[���PK��\�\�>>
��bug71626.phptnu�[���PK��\�Vb11%@�gmagick-081-separateimagechannel.phptnu�[���PK��\�Z4}��%��gmagick-121_deconstructGif_basic.phptnu�[���PK��\"5�@NN#��gmagickpixel-006-setcolorcount.phptnu�[���PK��\C%E�ll&x�gmagickdraw_034_setClipRule_basic.phptnu�[���PK��\��E�:�gmagick-088-getimageblob.phptnu�[���PK��\�aƽ77$��gmagick-031-set_getimagecompose.phptnu�[���PK��\(��/�gmagick-078-rollimage.phptnu�[���PK��\aGޯ��gmagick-085-stripimage.phptnu�[���PK��\"��{MM��gmagick-079-rotateimage.phptnu�[���PK��\Q�%G��:��gmagickdraw-031-setdashstrokearray_getdashstrokearray.phptnu�[���PK��\�T6���6z�gmagickdraw-013-setstrokecolor_getstrokecolor_q16.phptnu�[���PK��\3��))$��gmagick-022-flip_and_flop_image.phptnu�[���PK��\?*�� S�gmagick-089-getnumberimages.phptnu�[���PK��\V6B��#��gmagick-123_sharpenImage_basic.phptnu�[���PK��\�A�Qkk��gmagick-106-testConstants.phptnu�[���PK��\����"��gmagickdraw_041_popDefs_basic.phptnu�[���PK��\7��Y))��gmagick-068-modulateimage.phptnu�[���PK��\�Mx���2R�gmagickdraw-006-setfillcolor_getfillcolor_q16.phptnu�[���PK��\3j$��gmagick-117_haldClutImage_basic.phptnu�[���PK��\JP�gmagick-058-getversion.phptnu�[���PK��\��V���e�bug_71742.phptnu�[���PK��\41�.��!��gmagick-118_shaveImage_basic.phptnu�[���PK��\*�@a$$,v�gmagickdraw-009-setfontsize_getfontsize.phptnu�[���PK��\�\�yy(�gmagick-026-set_getimageblueprimary.phptnu�[���PK��\���;00"�gmagick-032-set_getimagedelay.phptnu�[���PK��\ܣJ���Igmagick-098-cloneimage.phptnu�[���PK��\ç|772`gmagick-025-set_and_getimageckgroundcolor_q16.phptnu�[���PK��\�q���gmagick-059-hasnextimage.phptnu�[���PK��\�Z؟�_	gmagick-006-annotateimage.phptnu�[���PK��\̐�N��Lgmagickdraw-024-scale.phptnu�[���PK��\\Dϟ��2Q
gmagickdraw-001-setstrokecolor_setstrokewidth.phptnu�[���PK��\��@:DD$:gmagickdraw-008-setfont_getfont.phptnu�[���PK��\������gmagickdraw-018-point.phptnu�[���PK��\�k�]::*�gmagickdraw-028-setlinecap_getlinecap.phptnu�[���PK��\|5�VLgmagick-030-getimagecolors.phptnu�[���PK��\O�O�&�gmagick-125_radialBlurImage_basic.phptnu�[���PK��\�#�cc)gmagickdraw_038_setFontStretch_basic.phptnu�[���PK��\N� ++"�gmagick-050-set_getimageunits.phptnu�[���PK��\���ؠ�M gmagickdraw-017-line.phptnu�[���PK��\��W++"6"gmagick-047-set_getimagescene.phptnu�[���PK��\5[��~~�#gmagickdraw-020-polyline.phptnu�[���PK��\����� ~'gmagick-002-crop_variations.phptnu�[���PK��\�
�%�)gmagick-004-composite_variations.phptnu�[���PK��\%��pp'�+gmagick-051-set_getimagewhitepoint.phptnu�[���PK��\Rȁ�-gmagick-020-enhanceimage.phptnu�[���PK��\R��{{/gmagick-007-blurimage.phptnu�[���PK��\v���0gmagick-064-magnifyimage.phptnu�[���PK��\ҿ�;;,62gmagickdraw-029-setlinejoin_getlinejoin.phptnu�[���PK��\א
��3gmagick-055-getreleasedate.phptnu�[���PK��\^&��	�	"65gmagick-129_gettersAndSetters.phptnu�[���PK��\�Gjj*_?gmagickdraw-026-setgravity_getgravity.phptnu�[���PK��\��5x#Agmagick-018-edgeimage.phptnu�[���PK��\���(uBgmagick-124__reduceNoiseImage_basic.phptnu�[���PK��\���*++'�Dgmagickpixel-001-setcolor_getcolor.phptnu�[���PK��\Z�|�� jGgmagick-054-getquantumdepth.phptnu�[���PK��\4�q����Jgmagick-128_staticMethods.phptnu�[���PK��\^���Lgmagick-119_thumbnail_fill.phptnu�[���PK��\`��-ll�Ngmagick-012-chopimage.phptnu�[���PK��\�ĜH440�Pgmagickdraw-011-setfontweight_getfontweight.phptnu�[���PK��\��h���-Rgmagick-108_adaptiveThresholdImage_basic.phptnu�[���PK��\����-- Ugmagick-008-addimage.phptnu�[���PK��\�Vr���Vgmagickdraw-003-annotate.phptnu�[���PK��\i@��+�Xgmagick-109_affineTransformImage_basic.phptnu�[���PK��\."&C**"�[gmagick-033-set_getimagedepth.phptnu�[���PK��\��i��B]gmagick-095-negateimage.phptnu�[���PK��\K�3pp(V^gmagick-046-set_getimagerresolution.phptnu�[���PK��\��
�44`gmagick-009-addimagenoise.phptnu�[���PK��\A�x�..%�agmagickdraw_043_setViewBox_basic.phptnu�[���PK��\�����$#ggmagick-115_equalizeImage_case2.phptnu�[���PK��\]�y��
jgmagickpixel-004-clone.phptnu�[���PK��\Yfe�(kgmagick-052-getimagewidth.phptnu�[���PK��\��v:���lgmagick-077-cropthumbnail.phptnu�[���PK��\H6�Ɔ�!Yugmagickdraw-032_circle_basic.phptnu�[���PK��\��
ff0zgmagickdraw-002-ellipse.phptnu�[���PK��\1)d��+�{gmagick-107_setimagevirtualpixelmethod.phptnu�[���PK��\n� �� �gmagick-116_waveImage_basic.phptnu�[���PK��\L��``"Q�gmagick-048-getimagesignature.phptnu�[���PK��\W[�OO�gmagick-082-shearimage.phptnu�[���PK��\�4��MM��gmagick-057-set_getsize.phptnu�[���PK��\�g��6�gmagick-094-thresholdimage.phptnu�[���PK��\C� *P�gmagick-110_blackThresholdImage_basic.phptnu�[���PK��\��[55&��gmagick-087-setcompressionquality.phptnu�[���PK��\k,����*A�gmagick-111_whiteThresholdImage_basic.phptnu�[���PK��\IZ��//��gmagickdraw-019-polygon.phptnu�[���PK��\�k��)�)
��hald_8.pngnu�[���PK��\�ȉ�$μgmagick-127_contrastImage_basic.phptnu�[���PK��\��d��$�gmagickdraw-022-rotate.phptnu�[���PK��\�t�M��1(�gmagickpixel-002-setcolorvalue_getcolorvalue.phptnu�[���PK��\�\�gSS,2�gmagick-045-set_getimagerenderingintent.phptnu�[���PK��\I*�ff$��gmagick-112_colorizeImage_basic.phptnu�[���PK��\ל��kk��gmagick-001-read_and_write.phptnu�[���PK��\{+�_��U�gmagick-120_writeImageFile.phptnu�[���PK��\����YYX�Vera.ttfnu�[���PK��\��P  ��gmagick-063-levelimage.phptnu�[���PK��\�d,�T�gmagick-069-trimimage.phptnu�[���PK��\0�z�44#��gmagickpixel-003-getcolorcount.phptnu�[���PK��\��.��� 7�gmagick-104-setgetimagepage.phptnu�[���PK�[�\|I�&6t6tR�RedisClusterTest.phpnu�[���PK�[�\8�X99�Imake-cluster.shnu�[���PK�[�\�c|rOrOD^RedisArrayTest.phpnu�[���PK�[�\�1D��startSession.phpnu�[���PK�[�\ϟv�&&
P�TestSuite.phpnu�[���PK�[�\�����	��mkring.shnu�[���PK�[�\?Ƿz00��regenerateSessionId.phpnu�[���PK�[�\�$�6
6

�TestRedis.phpnu�[���PK�[�\Ə-00��getSessionData.phpnu�[���PK�[�\��@�@
�RedisTest.phpnu�[���PK�[�\�m��P
P
44RedisSentinelTest.phpnu�[���PK���K�>