Skip to content

Commit

Permalink
commit phpunit case
Browse files Browse the repository at this point in the history
  • Loading branch information
何延龙 committed May 22, 2015
1 parent 7bc5337 commit 2252c50
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 23 deletions.
103 changes: 88 additions & 15 deletions Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@ class Connection extends Component


public $redisCommands = [
'PING'
];

public $redisWriteCommands = [
'SET',
];

public $redisReadCommands = [
'GET',
];

public function __sleep()
Expand Down Expand Up @@ -63,7 +71,7 @@ public function open()
if (!empty($this->slave)) {
foreach ($this->slave as $node) {
$connection = $this->connect($node);
array_push($this->_socket['master'], $connection);
array_push($this->_socket['slave'], $connection);
}
}
}
Expand Down Expand Up @@ -103,23 +111,83 @@ public function getLuaScriptBuilder()
public function __call($name, $params)
{
$redisCommand = strtoupper(Inflector::camel2words($name, false));
if (in_array($redisCommand, $this->redisCommands)) {
return $this->executeCommand($name, $params);
if (in_array($redisCommand, $this->redisWriteCommands)) {

$node = $this->_socket['master'][$this->getMasterNode($params[0])];
return $this->executeCommand($name, $params, $node);

} else if (in_array($redisCommand, $this->redisReadCommands)) {

$node = $this->_socket['master'][$this->getSlaveNode($params[0])];
return $this->executeCommand($name, $params, $node);

} else {
return parent::__call($name, $params);
}
}

public function executeCommand($name, $params = [])
public function executeCommand($name, $params = [], $node)
{
$this->open();
$socket = $node;

// TODO
array_unshift($params, $name);
$command = '*' . count($params) . "\r\n";

foreach ($params as $arg) {
$command .= '$' . mb_strlen($arg, '8bit') . "\r\n" . $arg . "\r\n";
}
\Yii::trace("Executing Redis Command: {$name}", __METHOD__);
fwrite($socket, $command);

return $this->parseResponse(implode(' ', $params), $socket);
}

private function parseResponse($command)
private function parseResponse($command, $socket)
{
// TODO
if (($line = fgets($socket)) === false) {
throw new Exception("Failed to read from socket.\nRedis command was: " . $command);
}

$type = $line[0];
$line = mb_substr($line, 1, -2, '8bit');

switch ($type) {
case '+': // Status reply
if ($line === 'OK' || $line === 'PONG') {
return true;
} else {
return $line;
}
case '-': // Error reply
throw new Exception("Redis error: " . $line . "\nRedis command was: " . $command);
case ':': // Integer reply
// no cast to int as it is in the range of a signed 64 bit integer
return $line;
case '$': // Bulk replies
if ($line == '-1') {
return null;
}
$length = $line + 2;
$data = '';
while ($length > 0) {
if (($block = fread($socket, $length)) === false) {
throw new Exception("Failed to read from socket.\nRedis command was: " . $command);
}
$data .= $block;
$length -= mb_strlen($block, '8bit');
}
return mb_substr($data, 0, -2, '8bit');
case '*': // Multi-bulk replies
$count = (int)$line;
$data = [];
for ($i = 0; $i < $count; $i++) {
$data[] = $this->parseResponse($command, $socket);
}
return $data;
default:
throw new Exception('Received illegal data from redis: ' . $line . "\nRedis command was: " . $command);
}
}

private function connect($node)
Expand All @@ -141,23 +209,28 @@ private function connect($node)
stream_set_timeout($socket, $timeout = (int)$this->dataTimeout, (int)(($this->dataTimeout - $timeout) * 1000000));
}

if ($this->password !== null) {
$this->executeCommand('AUTH', [$this->password]);
}

$this->executeCommand('SELECT', [$this->database]);
$this->initConnection();

} else {
\Yii::error("Failed to open redis DB connection ($connection): $errorNumber - $errorDescription", __CLASS__);
$message = YII_DEBUG ? "Failed to open redis DB connection ($connection): $errorNumber - $errorDescription" : 'Failed to open DB connection.';
throw new Exception($message, $errorDescription, (int)$errorNumber);
}

return $socket;
}

private function disconnect($node)
{
$this->executeCommand('QUIT');
//$this->executeCommand('QUIT');
stream_socket_shutdown($node, STREAM_SHUT_RDWR);
}

public function getMasterNode($name)
{
return (abs(crc32($name)) % (count($this->_socket['master']))) + 1;
}

public function getSlaveNode($name)
{
return (abs(crc32($name)) % (count($this->_socket['slave']))) + 1;
}
}
13 changes: 13 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit bootstrap="./tests/bootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnFailure="false">
<testsuites>
<testsuite name="Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
</phpunit>
24 changes: 23 additions & 1 deletion tests/RedisConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,31 @@
* Date: 2015/5/21
* Time: 13:58
*/
use heyanlong\redis\tests;

namespace heyanlong\redis\tests;

class RedisConnectionTest extends TestCase
{
public function testConnect()
{
$db = $this->getConnection(false);
$database = $db->database;

$db->open();
//$this->assertTrue($db->ping());
$db->set('TESTKEY', 'TESTVALUE');
$db->close();

$db = $this->getConnection(false);
$db->database = $database;
$db->open();
$this->assertEquals('TESTVALUE', $db->get('TESTKEY'));
$db->close();

// $db = $this->getConnection(false);
// $db->database = 1;
// $db->open();
// $this->assertNull($db->get('TESTKEY'));
// $db->close();
}
}
113 changes: 106 additions & 7 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,111 @@
<?php

/**
* Created by PhpStorm.
* User: heyanlong
* Date: 2015/5/21
* Time: 13:59
*/
class TestCase extends \PHPUnit_Framework_TestCase
namespace heyanlong\redis\tests;

use Yii;
use yii\di\Container;
use heyanlong\redis\Connection;
use yii\helpers\ArrayHelper;

abstract class TestCase extends \PHPUnit_Framework_TestCase
{
public static $params;


/**
* Returns a test configuration param from /data/config.php
* @param string $name params name
* @param mixed $default default value to use when param is not set.
* @return mixed the value of the configuration param
*/
public static function getParam($name, $default = null)
{
if (static::$params === null) {
static::$params = require(__DIR__ . '/data/config.php');
}

return isset(static::$params[$name]) ? static::$params[$name] : $default;
}

/**
* Clean up after test.
* By default the application created with [[mockApplication]] will be destroyed.
*/
protected function tearDown()
{
parent::tearDown();
$this->destroyApplication();
}

/**
* Populates Yii::$app with a new application
* The application will be destroyed on tearDown() automatically.
* @param array $config The application configuration, if needed
* @param string $appClass name of the application class to create
*/
protected function mockApplication($config = [], $appClass = '\yii\console\Application')
{
new $appClass(ArrayHelper::merge([
'id' => 'testapp',
'basePath' => __DIR__,
'vendorPath' => dirname(__DIR__) . '/vendor',
], $config));
}

protected function mockWebApplication($config = [], $appClass = '\yii\web\Application')
{
new $appClass(ArrayHelper::merge([
'id' => 'testapp',
'basePath' => __DIR__,
'vendorPath' => dirname(__DIR__) . '/vendor',
'components' => [
'request' => [
'cookieValidationKey' => 'wefJDF8sfdsfSDefwqdxj9oq',
'scriptFile' => __DIR__ . '/index.php',
'scriptUrl' => '/index.php',
],
]
], $config));
}

/**
* Destroys application in Yii::$app by setting it to null.
*/
protected function destroyApplication()
{
Yii::$app = null;
Yii::$container = new Container();
}

protected function setUp()
{
$databases = self::getParam('databases');
$params = isset($databases['redis']) ? $databases['redis'] : null;
if ($params === null) {
$this->markTestSkipped('No redis server connection configured.');
}

$connection = new Connection($params);

$this->mockApplication(['components' => ['redis' => $connection]]);

parent::setUp();
}

/**
* @param boolean $reset whether to clean up the test database
* @return Connection
*/
public function getConnection($reset = true)
{
$databases = self::getParam('databases');
$params = isset($databases['redis']) ? $databases['redis'] : [];
$db = new Connection($params);
if ($reset) {
$db->open();
$db->flushdb();
}

return $db;
}
}
17 changes: 17 additions & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

error_reporting(-1);

define('YII_ENABLE_ERROR_HANDLER', false);
define('YII_DEBUG', true);
$_SERVER['SCRIPT_NAME'] = '/' . __DIR__;
$_SERVER['SCRIPT_FILENAME'] = __FILE__;

require_once(__DIR__ . '/../../vendor/autoload.php');
require_once(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
require_once(__DIR__ . '/TestCase.php');
require_once(__DIR__ . '/../Connection.php');


Yii::setAlias('@yiiunit/extensions/redis', __DIR__);
Yii::setAlias('@yii/redis', dirname(__DIR__));
35 changes: 35 additions & 0 deletions tests/data/config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

/**
* This is the configuration file for the Yii2 unit tests.
* You can override configuration values by creating a `config.local.php` file
* and manipulate the `$config` variable.
* For example to change MySQL username and password your `config.local.php` should
* contain the following:
*
*/

$config = [
'databases' => [
'redis' => [
'master' => [
// '10.155.20.167:6391',
// '10.155.20.168:6379',
// '10.155.20.167:6380',
// '10.155.20.169:6379',
'localhost:6379',
],
'slave' => [
// '10.155.20.167:6379',
// '10.155.20.168:6380',
// '10.155.20.168:6391',
// '10.155.20.169:6380',
'localhost:6379',
],
'database' => 0,
'password' => null,
],
],
];

return $config;

0 comments on commit 2252c50

Please sign in to comment.