More test refactoring.

* Switch remaining old-style PHP 5.4 `Array(...)` declarations to `[...]`
* Update variable names getting rid hungarian notation prefixes (e.g.
  `str_`, `i_`, etc).
* Allow cluster seeds to be passed on the command-line instead of soley
  relying on either a node environment variable or our
  tests/nodes/nodemap file.  This should make it easier to run ad-hoc
  cluster tests by specifying just a single seed.
* Add some diagnostics for when we can't find a suitable cluster to run
  our tests against indicating exactly where we looked for the env var
  and node file.
* Refactor RedisArray tests to use our newer TestSuite assertions.
* Allow `RedisArray` ports to be specified on the command-line as well.
* Various formatting fixes.
* More robust KeyDB detection.
This commit is contained in:
michael-grunder
2024-05-29 22:12:15 -07:00
committed by Michael Grunder
parent 0d89e92889
commit 78b70ca8f4
7 changed files with 906 additions and 831 deletions
+1 -1
View File
@@ -121,7 +121,7 @@ For instance, the keys “{user:1}:name” and “{user:1}:email” will be stor
## Custom key distribution function
In order to control the distribution of keys by hand, you can provide a custom function or closure that returns the server number, which is the index in the array of servers that you created the RedisArray object with.
For instance, instantiate a RedisArray object with `new RedisArray(array("us-host", "uk-host", "de-host"), array("distributor" => "dist"));` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server.
For instance, instantiate a RedisArray object with `new RedisArray(["us-host", "uk-host", "de-host"], ["distributor" => "dist"]);` and write a function called "dist" that will return `2` for all the keys that should end up on the "de-host" server.
### Example
<pre>
+1 -1
View File
@@ -24,7 +24,7 @@ $obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true
// Connect with cluster using SSL/TLS
// last argument is an array with [SSL context](https://www.php.net/manual/en/context.ssl.php) options
$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, NULL, Array("verify_peer" => false));
$obj_cluster = new RedisCluster(NULL, ["host:7000", "host:7001"], 1.5, 1.5, true, NULL, ["verify_peer" => false]);
```
#### Loading a cluster configuration by name
+111 -88
View File
@@ -2,6 +2,7 @@
require_once(dirname($_SERVER['PHP_SELF'])."/TestSuite.php");
define('REDIS_ARRAY_DATA_SIZE', 1000);
define('REDIS_RA_DEFAULT_PORTS', [6379, 6380, 6381, 6382]);
function custom_hash($str) {
// str has the following format: $APPID_fb$FACEBOOKID_$key.
@@ -18,19 +19,19 @@ function parseHostPort($str, &$host, &$port) {
$port = substr($str, $pos+1);
}
function getRedisVersion($obj_r) {
$arr_info = $obj_r->info();
function getRedisVersion(object $client) {
$arr_info = $client->info();
if (!$arr_info || !isset($arr_info['redis_version'])) {
return "0.0.0";
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));
function getMinVersion(object $ra) {
$min_version = '0.0.0';
foreach ($ra->_hosts() as $host) {
$version = getRedisVersion($ra->_instance($host));
if (version_compare($version, $min_version) > 0) {
$min_version = $version;
}
@@ -43,7 +44,7 @@ class Redis_Array_Test extends TestSuite
{
private $min_version;
private $strings;
public $ra = NULL;
public $ra = NULL;
private $data = NULL;
public function setUp() {
@@ -54,13 +55,13 @@ class Redis_Array_Test extends TestSuite
$this->strings['key-'.$i] = 'val-'.$i;
}
global $newRing, $oldRing, $useIndex;
$options = ['previous' => $oldRing, 'index' => $useIndex];
global $new_ring, $old_ring, $use_index;
$options = ['previous' => $old_ring, 'index' => $use_index];
if ($this->getAuth()) {
$options['auth'] = $this->getAuth();
}
$this->ra = new RedisArray($newRing, $options);
$this->ra = new RedisArray($new_ring, $options);
$this->min_version = getMinVersion($this->ra);
}
@@ -125,12 +126,16 @@ class Redis_Array_Test extends TestSuite
$this->checkCommonLocality();
// with common hashing function
global $newRing, $oldRing, $useIndex;
$options = ['previous' => $oldRing, 'index' => $useIndex, 'function' => 'custom_hash'];
global $new_ring, $old_ring, $use_index;
$options = [
'previous' => $old_ring,
'index' => $use_index,
'function' => 'custom_hash'
];
if ($this->getAuth()) {
$options['auth'] = $this->getAuth();
}
$this->ra = new RedisArray($newRing, $options);
$this->ra = new RedisArray($new_ring, $options);
// basic key locality with custom hash
$this->addData('fb'.rand());
@@ -140,20 +145,27 @@ class Redis_Array_Test extends TestSuite
public function customDistributor($key)
{
$a = unpack("N*", md5($key, true));
global $newRing;
$pos = abs($a[1]) % count($newRing);
global $new_ring;
$pos = abs($a[1]) % count($new_ring);
return $pos;
}
public function testKeyDistributor()
{
global $newRing, $useIndex;
$options = ['index' => $useIndex, 'function' => 'custom_hash', 'distributor' => [$this, "customDistributor"]];
global $new_ring, $useIndex;
$options = [
'index' => $useIndex,
'function' => 'custom_hash',
'distributor' => [$this, "customDistributor"]
];
if ($this->getAuth()) {
$options['auth'] = $this->getAuth();
}
$this->ra = new RedisArray($newRing, $options);
$this->ra = new RedisArray($new_ring, $options);
// custom key distribution function.
$this->addData('fb'.rand());
@@ -163,7 +175,7 @@ class Redis_Array_Test extends TestSuite
foreach($this->data as $k => $v) {
$node = $this->ra->_target($k);
$pos = $this->customDistributor($k);
$this->assertEquals($node, $newRing[$pos]);
$this->assertEquals($node, $new_ring[$pos]);
}
}
@@ -254,20 +266,23 @@ class Redis_Rehashing_Test extends TestSuite
$this->zsets['zset-'.$i] = [$i, 'A', $i+1, 'B', $i+2, 'C', $i+3, 'D', $i+4, 'E'];
}
global $newRing, $oldRing, $useIndex;
$options = ['previous' => $oldRing, 'index' => $useIndex];
global $new_ring, $old_ring, $useIndex;
$options = [
'previous' => $old_ring,
'index' => $useIndex
];
if ($this->getAuth()) {
$options['auth'] = $this->getAuth();
}
// create array
$this->ra = new RedisArray($newRing, $options);
$this->ra = new RedisArray($new_ring, $options);
$this->min_version = getMinVersion($this->ra);
}
public function testFlush() {
// flush all servers first.
global $serverList;
foreach($serverList as $s) {
global $server_list;
foreach($server_list as $s) {
parseHostPort($s, $host, $port);
$r = new Redis();
@@ -275,7 +290,7 @@ class Redis_Rehashing_Test extends TestSuite
if ($this->getAuth()) {
$this->assertTrue($r->auth($this->getAuth()));
}
$r->flushdb();
$this->assertTrue($r->flushdb());
}
}
@@ -327,28 +342,24 @@ class Redis_Rehashing_Test extends TestSuite
foreach($this->sets as $k => $v) {
$ret = $this->ra->smembers($k); // get values
// sort sets
sort($v);
sort($ret);
$this->assertTrue($ret == $v);
$this->assertEqualsWeak($v, $ret);
}
// lists
foreach($this->lists as $k => $v) {
$ret = $this->ra->lrange($k, 0, -1);
$this->assertTrue($ret == $v);
$this->assertEqualsWeak($v, $ret);
}
// hashes
foreach($this->hashes as $k => $v) {
$ret = $this->ra->hgetall($k); // get values
$this->assertTrue($ret == $v);
$this->assertEqualsWeak($v, $ret);
}
// sorted sets
foreach($this->zsets as $k => $v) {
$ret = $this->ra->zrange($k, 0, -1, TRUE); // get values with scores
$ret = $this->ra->zrange($k, 0, -1, TRUE);
// create assoc array from local dataset
$tmp = [];
@@ -357,16 +368,15 @@ class Redis_Rehashing_Test extends TestSuite
}
// compare to RA value
$this->assertTrue($ret == $tmp);
$this->assertEqualsWeak($tmp, $ret);
}
}
// 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.
global $new_ring, $old_ring, $server_list;
$old_ring = $new_ring; // back up the original.
$new_ring = $server_list; // add a new node to the main ring.
}
public function testReadUsingFallbackMechanism() {
@@ -382,7 +392,7 @@ class Redis_Rehashing_Test extends TestSuite
$this->ra->_rehash(function ($host, $count) use (&$total) {
$total += $count;
});
$this->assertTrue($total > 0);
$this->assertGT(0, $total);
}
public function testReadRedistributedKeys() {
@@ -407,13 +417,17 @@ class Redis_Auto_Rehashing_Test extends TestSuite {
$this->strings['key-'.$i] = 'val-'.$i;
}
global $newRing, $oldRing, $useIndex;
$options = ['previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE];
global $new_ring, $old_ring, $useIndex;
$options = [
'previous' => $old_ring,
'index' => $useIndex,
'autorehash' => TRUE
];
if ($this->getAuth()) {
$options['auth'] = $this->getAuth();
}
// create array
$this->ra = new RedisArray($newRing, $options);
$this->ra = new RedisArray($new_ring, $options);
$this->min_version = getMinVersion($this->ra);
}
@@ -437,9 +451,9 @@ class Redis_Auto_Rehashing_Test extends TestSuite {
// 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.
global $new_ring, $old_ring, $server_list;
$old_ring = $new_ring; // back up the original.
$new_ring = $server_list; // add a new node to the main ring.
}
// Read and migrate keys on fallback, causing the whole ring to be rehashed.
@@ -458,24 +472,29 @@ class Redis_Auto_Rehashing_Test extends TestSuite {
$this->assertTrue($r->auth($this->getAuth()));
}
$this->assertEquals($v, $r->get($k)); // check that the key has actually been migrated to the new node.
// check that the key has actually been migrated to the new node.
$this->assertEquals($v, $r->get($k));
}
}
}
// Test node-specific multi/exec
class Redis_Multi_Exec_Test extends TestSuite {
public $ra = NULL;
private $min_version;
public $ra = NULL;
private static $new_group = NULL;
private static $new_salary = NULL;
public function setUp() {
global $newRing, $oldRing, $useIndex;
$options = ['previous' => $oldRing, 'index' => $useIndex];
global $new_ring, $old_ring, $useIndex;
$options = ['previous' => $old_ring, 'index' => $useIndex];
if ($this->getAuth()) {
$options['auth'] = $this->getAuth();
}
// create array
$this->ra = new RedisArray($newRing, $options);
$this->ra = new RedisArray($new_ring, $options);
$this->min_version = getMinVersion($this->ra);
}
@@ -501,49 +520,47 @@ class Redis_Multi_Exec_Test extends TestSuite {
}
public function testMultiExec() {
// Joe gets a promotion
$newGroup = $this->ra->get('{groups}:executives');
$newSalary = 4000;
self::$new_group = $this->ra->get('{groups}:executives');
self::$new_salary = 4000;
// change both in a transaction.
$host = $this->ra->_target('{employee:joe}'); // transactions are per-node, so we need a reference to it.
// transactions are per-node, so we need a reference to it.
$host = $this->ra->_target('{employee:joe}');
$this->ra->multi($host)
->set('1_{employee:joe}_group', $newGroup)
->set('1_{employee:joe}_salary', $newSalary)
->set('1_{employee:joe}_group', self::$new_group)
->set('1_{employee:joe}_salary', self::$new_salary)
->exec();
// check that the group and salary have been changed
$this->assertEquals($newGroup, $this->ra->get('1_{employee:joe}_group'));
$this->assertEqualsWeak($newSalary, $this->ra->get('1_{employee:joe}_salary'));
$this->assertEquals(self::$new_group, $this->ra->get('1_{employee:joe}_group'));
$this->assertEqualsWeak(self::$new_salary, $this->ra->get('1_{employee:joe}_salary'));
}
public function testMultiExecMSet() {
global $newGroup, $newSalary;
$newGroup = 1;
$newSalary = 10000;
self::$new_group = 1;
self::$new_salary = 10000;
// test MSET, making Joe a top-level executive
$out = $this->ra->multi($this->ra->_target('{employee:joe}'))
->mset(['1_{employee:joe}_group' => $newGroup, '1_{employee:joe}_salary' => $newSalary])
->mset([
'1_{employee:joe}_group' => self::$new_group,
'1_{employee:joe}_salary' => self::$new_salary
])
->exec();
$this->assertTrue($out[0]);
}
public function testMultiExecMGet() {
global $newGroup, $newSalary;
// test MGET
$out = $this->ra->multi($this->ra->_target('{employee:joe}'))
->mget(['1_{employee:joe}_group', '1_{employee:joe}_salary'])
->exec();
$this->assertTrue($out[0][0] == $newGroup);
$this->assertTrue($out[0][1] == $newSalary);
$this->assertEqualsWeak(self::$new_group, $out[0][0]);
$this->assertEqualsWeak(self::$new_salary, $out[0][1]);
}
public function testMultiExecDel() {
@@ -613,13 +630,17 @@ class Redis_Distributor_Test extends TestSuite {
private $min_version;
public function setUp() {
global $newRing, $oldRing, $useIndex;
$options = ['previous' => $oldRing, 'index' => $useIndex, 'distributor' => [$this, 'distribute']];
global $new_ring, $old_ring, $useIndex;
$options = [
'previous' => $old_ring,
'index' => $useIndex,
'distributor' => [$this, 'distribute']
];
if ($this->getAuth()) {
$options['auth'] = $this->getAuth();
}
// create array
$this->ra = new RedisArray($newRing, $options);
$this->ra = new RedisArray($new_ring, $options);
$this->min_version = getMinVersion($this->ra);
}
@@ -640,28 +661,30 @@ class Redis_Distributor_Test extends TestSuite {
}
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');
$UK_server = $this->ra->_target('{uk}test');
$US_server = $this->ra->_target('{us}test');
$DE_server = $this->ra->_target('{de}test');
$XX_server = $this->ra->_target('{xx}test');
$nodes = $this->ra->_hosts();
$this->assertEquals($ukServer, $nodes[0]);
$this->assertEquals($usServer,$nodes[1]);
$this->assertEquals($deServer,$nodes[2]);
$this->assertEquals($defaultServer, $nodes[2]);
$this->assertEquals($UK_server, $nodes[0]);
$this->assertEquals($US_server, $nodes[1]);
$this->assertEquals($DE_server, $nodes[2]);
$this->assertEquals($XX_server, $nodes[2]);
}
}
function run_tests($className, $str_filter, $str_host, $auth) {
// reset rings
global $newRing, $oldRing, $serverList;
function run_ra_tests($test_class, $filter, $host, array $full_ring,
array $sub_ring, $auth)
{
global $new_ring, $old_ring, $server_list;
$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);
$server_list = $full_ring;
$new_ring = $sub_ring;
$old_ring = [];
return TestSuite::run($test_class, $filter, $host, NULL, $auth);
}
?>
+265 -211
View File
@@ -7,7 +7,7 @@ require_once(dirname($_SERVER['PHP_SELF'])."/RedisTest.php");
* where we're validating specific cluster mechanisms
*/
class Redis_Cluster_Test extends Redis_Test {
private $_arr_redis_types = [
private $redis_types = [
Redis::REDIS_STRING,
Redis::REDIS_SET,
Redis::REDIS_LIST,
@@ -15,13 +15,17 @@ class Redis_Cluster_Test extends Redis_Test {
Redis::REDIS_HASH
];
private $_arr_failover_types = [
private $failover_types = [
RedisCluster::FAILOVER_NONE,
RedisCluster::FAILOVER_ERROR,
RedisCluster::FAILOVER_DISTRIBUTE
];
protected static $_arr_node_map = [];
protected static array $seeds = [];
private static array $seed_messages = [];
private static string $seed_source = '';
/* Tests we'll skip all together in the context of RedisCluster. The
* RedisCluster class doesn't implement specialized (non-redis) commands
@@ -61,37 +65,85 @@ class Redis_Cluster_Test extends Redis_Test {
public function testSession_noUnlockOfOtherProcess() { $this->markTestSkipped(); }
public function testSession_lockWaitTime() { $this->markTestSkipped(); }
/* Load our seeds on construction */
public function __construct($str_host, $i_port, $str_auth) {
parent::__construct($str_host, $i_port, $str_auth);
self::$_arr_node_map = array_filter(explode(' ', getenv('REDIS_CLUSTER_NODES')));
/* Store our node map */
if (!self::$_arr_node_map) {
$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);
}
self::$_arr_node_map = array_filter(
explode("\n", file_get_contents($str_nodemap_file)
));
private function loadSeedsFromHostPort($host, $port) {
try {
$rc = new RedisCluster(NULL, ["$host:$port"], 1, 1, true, $this->getAuth());
self::$seed_source = "Host: $host, Port: $port";
return array_map(function($master) {
return sprintf('%s:%s', $master[0], $master[1]);
}, $rc->_masters());
} catch (Exception $ex) {
/* fallthrough */
}
self::$seed_messages[] = "--host=$host, --port=$port";
return false;
}
private function loadSeedsFromEnv() {
$seeds = getenv('REDIS_CLUSTER_NODES');
if ( ! $seeds) {
self::$seed_messages[] = "environment variable REDIS_CLUSTER_NODES ($seeds)";
return false;
}
self::$seed_source = 'Environment variable REDIS_CLUSTER_NODES';
return array_filter(explode(' ', $seeds));
}
private function loadSeedsFromNodeMap() {
$nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap';
if ( ! file_exists($nodemap_file)) {
self::$seed_messages[] = "nodemap file '$nodemap_file'";
return false;
}
self::$seed_source = "Nodemap file '$nodemap_file'";
return array_filter(explode("\n", file_get_contents($nodemap_file)));
}
private function loadSeeds($host, $port) {
if (($seeds = $this->loadSeedsFromNodeMap()))
return $seeds;
if (($seeds = $this->loadSeedsFromEnv()))
return $seeds;
if (($seeds = $this->loadSeedsFromHostPort($host, $port)))
return $seeds;
fprintf(STDERR, "Error: Unable to load seeds for RedisCluster tests\n");
foreach (self::$seed_messages as $msg) {
fprintf(STDERR, " Tried: %s\n", $msg);
}
exit(1);
}
/* Load our seeds on construction */
public function __construct($host, $port, $auth) {
parent::__construct($host, $port, $auth);
self::$seeds = $this->loadSeeds($host, $port);
}
/* 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');
$this->is_keydb = $this->redis->info('keydb') !== false;
$this->redis = $this->newInstance();
$info = $this->redis->info(uniqid());
$this->version = $info['redis_version'] ?? '0.0.0';
$this->is_keydb = $this->detectKeyDB($info);
}
/* Override newInstance as we want a RedisCluster object */
protected function newInstance() {
return new RedisCluster(NULL, self::$_arr_node_map, 30, 30, true, $this->getAuth());
try {
return new RedisCluster(NULL, self::$seeds, 30, 30, true, $this->getAuth());
} catch (Exception $ex) {
fprintf(STDERR, "Fatal error: %s\n", $ex->getMessage());
fprintf(STDERR, "Seeds: %s\n", implode(' ', self::$seeds));
fprintf(STDERR, "Seed source: %s\n", self::$seed_source);
exit(1);
}
}
/* Overrides for RedisTest where the function signature is different. This
@@ -107,7 +159,7 @@ class Redis_Cluster_Test extends Redis_Test {
/* Make sure both variations work in MULTI mode */
$this->redis->multi();
$this->redis->ping('{ping-test}');
$this->redis->ping('{ping-test}','BEEP');
$this->redis->ping('{ping-test}', 'BEEP');
$this->assertEquals([true, 'BEEP'], $this->redis->exec());
}
@@ -138,7 +190,7 @@ class Redis_Cluster_Test extends Redis_Test {
$this->redis->sadd('some-item', 2);
$this->redis->sadd('some-item', 3);
$this->assertEquals(['1','2','3'], $this->redis->sort('some-item'));
$this->assertEquals(['1', '2', '3'], $this->redis->sort('some-item'));
// Kill our set/prefix
$this->redis->del('some-item');
@@ -147,15 +199,15 @@ class Redis_Cluster_Test extends Redis_Test {
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));
$key = "key:$i";
$this->assertTrue($this->redis->flushdb($key));
$this->redis->set($key, "val:$i");
$this->assertEquals(1, $this->redis->dbsize($key));
}
}
public function testInfo() {
$arr_check_keys = [
$fields = [
"redis_version", "arch_bits", "uptime_in_seconds", "uptime_in_days",
"connected_clients", "connected_slaves", "used_memory",
"total_connections_received", "total_commands_processed",
@@ -163,113 +215,113 @@ class Redis_Cluster_Test extends Redis_Test {
];
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]));
$info = $this->redis->info($i);
foreach ($fields as $field) {
$this->assertArrayKey($info, $field);
}
}
}
public function testClient() {
$str_key = 'key-' . rand(1,100);
$key = 'key-' . rand(1, 100);
$this->assertTrue($this->redis->client($str_key, 'setname', 'cluster_tests'));
$this->assertTrue($this->redis->client($key, 'setname', 'cluster_tests'));
$arr_clients = $this->redis->client($str_key, 'list');
$this->assertTrue(is_array($arr_clients));
$clients = $this->redis->client($key, 'list');
$this->assertIsArray($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'];
$addr = NULL;
foreach ($clients as $client) {
if ($client['name'] == 'cluster_tests') {
$addr = $client['addr'];
break;
}
}
/* We should be in there */
$this->assertFalse(empty($str_addr));
$this->assertIsString($addr);
/* Kill our own client! */
$this->assertTrue($this->redis->client($str_key, 'kill', $str_addr));
$this->assertTrue($this->redis->client($key, 'kill', $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]));
[$sec, $usec] = $this->redis->time(uniqid());
$this->assertEquals(strval(intval($sec)), strval($sec));
$this->assertEquals(strval(intval($usec)), strval($usec));
}
public function testScan() {
$i_key_count = 0;
$i_scan_count = 0;
$key_count = 0;
$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) {
foreach ($this->redis->_masters() as $master) {
/* Grab the number of keys we have */
$i_key_count += $this->redis->dbsize($arr_master);
$key_count += $this->redis->dbsize($master);
/* Scan the keys here */
$it = NULL;
while ($arr_keys = $this->redis->scan($it, $arr_master)) {
$i_scan_count += count($arr_keys);
while ($keys = $this->redis->scan($it, $master)) {
$scan_count += count($keys);
}
}
/* Our total key count should match */
$this->assertEquals($i_scan_count, $i_key_count);
$this->assertEquals($scan_count, $key_count);
}
public function testScanPrefix() {
$arr_prefixes = ['prefix-a:', 'prefix-b:'];
$str_id = uniqid();
$prefixes = ['prefix-a:', 'prefix-b:'];
$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;
foreach ($prefixes as $prefix) {
$this->redis->setOption(Redis::OPT_PREFIX, $prefix);
$this->redis->set($id, "LOLWUT");
$arr_keys[$prefix] = $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 ($prefixes as $prefix) {
$prefix_keys = [];
$this->redis->setOption(Redis::OPT_PREFIX, $prefix);
foreach ($this->redis->_masters() as $arr_master) {
foreach ($this->redis->_masters() as $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;
while ($keys = $this->redis->scan($it, $master, "*$id*")) {
foreach ($keys as $key) {
$prefix_keys[$prefix] = $key;
}
}
}
$this->assertTrue(count($arr_prefix_keys) == 1 && isset($arr_prefix_keys[$str_prefix]));
$this->assertIsArray($prefix_keys, 1);
$this->assertArrayKey($prefix_keys, $prefix);
}
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NOPREFIX);
$arr_scan_keys = [];
$scan_keys = [];
foreach ($this->redis->_masters() as $arr_master) {
foreach ($this->redis->_masters() as $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;
while ($keys = $this->redis->scan($it, $master, "*$id*")) {
foreach ($keys as $key) {
$scan_keys[] = $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));
foreach ($arr_keys as $prefix => $id) {
$this->assertInArray("{$prefix}{$id}", $scan_keys);
}
}
@@ -278,19 +330,19 @@ class Redis_Cluster_Test extends Redis_Test {
public function testPubSub() {
// PUBSUB CHANNELS ...
$result = $this->redis->pubsub("somekey", "channels", "*");
$this->assertTrue(is_array($result));
$this->assertIsArray($result);
$result = $this->redis->pubsub("somekey", "channels");
$this->assertTrue(is_array($result));
$this->assertIsArray($result);
// PUBSUB NUMSUB
$c1 = '{pubsub}-' . rand(1,100);
$c2 = '{pubsub}-' . rand(1,100);
$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->assertIsArray($result);
$this->assertEquals(4, count($result));
$arr_zipped = [];
@@ -301,13 +353,13 @@ class Redis_Cluster_Test extends Redis_Test {
// Make sure the elements are correct, and have zero counts
foreach([$c1,$c2] as $channel) {
$this->assertTrue(isset($result[$channel]));
$this->assertArrayKey($result, $channel);
$this->assertEquals(0, $result[$channel]);
}
// PUBSUB NUMPAT
$result = $this->redis->pubsub("somekey", "numpat");
$this->assertTrue(is_int($result));
$this->assertIsInt($result);
// Invalid call
$this->assertFalse($this->redis->pubsub("somekey", "notacommand"));
@@ -317,15 +369,15 @@ class Redis_Cluster_Test extends Redis_Test {
* 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->redis->del('x', 'y', 'z');
$ret = $this->redis->msetnx(['x'=>'a', 'y'=>'b', 'z'=>'c']);
$this->assertIsArray($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));
$ret = $this->redis->msetnx(['x'=>'a', 'y'=>'b', 'z'=>'c']);
$this->assertIsArray($ret);
$this->assertEquals(1, array_sum($ret));
$this->assertFalse($this->redis->msetnx([])); // set ø → FALSE
@@ -333,24 +385,23 @@ class Redis_Cluster_Test extends Redis_Test {
/* Slowlog needs to take a key or [ip, port], to direct it to a node */
public function testSlowlog() {
$str_key = uniqid() . '-' . rand(1, 1000);
$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'));
$this->assertIsArray($this->redis->slowlog($key, 'get'));
$this->assertIsArray($this->redis->slowlog($key, 'get', 10));
$this->assertIsInt($this->redis->slowlog($key, 'len'));
$this->assertTrue($this->redis->slowlog($key, 'reset'));
$this->assertFalse($this->redis->slowlog($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");
$info = $this->redis->info(uniqid(), "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);
$this->assertIsArray($info);
if (is_array($info)) {
foreach($info as $k => $value) {
$this->assertStringContains('cmdstat_', $k);
}
}
}
@@ -379,13 +430,10 @@ class Redis_Cluster_Test extends Redis_Test {
$this->assertEquals(['44'], $ret);
}
public function testDiscard()
{
/* start transaction */
public function testDiscard() {
$this->redis->multi();
/* Set and get in our transaction */
$this->redis->set('pipecount','over9000')->get('pipecount');
$this->redis->set('pipecount', 'over9000');
$this->redis->get('pipecount');
$this->assertTrue($this->redis->discard());
}
@@ -393,10 +441,10 @@ class Redis_Cluster_Test extends Redis_Test {
/* 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);
$key = uniqid() . '-' . rand(1, 1000);
// Flush any scripts we have
$this->assertTrue($this->redis->script($str_key, 'flush'));
$this->assertTrue($this->redis->script($key, 'flush'));
// Silly scripts to test against
$s1_src = 'return 1';
@@ -407,31 +455,31 @@ class Redis_Cluster_Test extends Redis_Test {
$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);
$result = $this->redis->script($key, 'exists', $s1_sha, $s2_sha, $s3_sha);
$this->assertIsArray($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);
$this->assertEquals($s1_sha, $this->redis->script($key, 'load', $s1_src));
$this->assertEquals($s2_sha, $this->redis->script($key, 'load', $s2_src));
$this->assertEquals($s3_sha, $this->redis->script($key, 'load', $s3_src));
// They should all exist
$result = $this->redis->script($str_key, 'exists', $s1_sha, $s2_sha, $s3_sha);
$result = $this->redis->script($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);
$key = uniqid() . '-' . rand(1, 1000);
// Flush any loaded scripts
$this->redis->script($str_key, 'flush');
$this->redis->script($key, 'flush');
// Non existent 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);
$this->assertFalse($this->redis->evalsha(sha1(uniqid()),[$key], 1));
$this->assertFalse($this->redis->evalsha('some-random-data'),[$key], 1);
// Load a script
$cb = uniqid(); // To ensure the script is new
@@ -439,69 +487,69 @@ class Redis_Cluster_Test extends Redis_Test {
$sha = sha1($scr);
// Run it when it doesn't exist, run it with eval, and then run it with sha1
$this->assertFalse($this->redis->evalsha($scr,[$str_key], 1));
$this->assertEquals(1, $this->redis->eval($scr,[$str_key], 1));
$this->assertEquals(1, $this->redis->evalsha($sha,[$str_key], 1));
$this->assertFalse($this->redis->evalsha($scr,[$key], 1));
$this->assertEquals(1, $this->redis->eval($scr,[$key], 1));
$this->assertEquals(1, $this->redis->evalsha($sha,[$key], 1));
}
public function testEvalBulkResponse() {
$str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
$str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';
$key1 = uniqid() . '-' . rand(1, 1000) . '{hash}';
$key2 = uniqid() . '-' . rand(1, 1000) . '{hash}';
$this->redis->script($str_key1, 'flush');
$this->redis->script($str_key2, 'flush');
$this->redis->script($key1, 'flush');
$this->redis->script($key2, 'flush');
$scr = "return {KEYS[1],KEYS[2]}";
$result = $this->redis->eval($scr,[$str_key1, $str_key2], 2);
$result = $this->redis->eval($scr,[$key1, $key2], 2);
$this->assertEquals($str_key1, $result[0]);
$this->assertEquals($str_key2, $result[1]);
$this->assertEquals($key1, $result[0]);
$this->assertEquals($key2, $result[1]);
}
public function testEvalBulkResponseMulti() {
$str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
$str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';
$key1 = uniqid() . '-' . rand(1, 1000) . '{hash}';
$key2 = uniqid() . '-' . rand(1, 1000) . '{hash}';
$this->redis->script($str_key1, 'flush');
$this->redis->script($str_key2, 'flush');
$this->redis->script($key1, 'flush');
$this->redis->script($key2, 'flush');
$scr = "return {KEYS[1],KEYS[2]}";
$this->redis->multi();
$this->redis->eval($scr, [$str_key1, $str_key2], 2);
$this->redis->eval($scr, [$key1, $key2], 2);
$result = $this->redis->exec();
$this->assertEquals($str_key1, $result[0][0]);
$this->assertEquals($str_key2, $result[0][1]);
$this->assertEquals($key1, $result[0][0]);
$this->assertEquals($key2, $result[0][1]);
}
public function testEvalBulkEmptyResponse() {
$str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
$str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';
$key1 = uniqid() . '-' . rand(1, 1000) . '{hash}';
$key2 = uniqid() . '-' . rand(1, 1000) . '{hash}';
$this->redis->script($str_key1, 'flush');
$this->redis->script($str_key2, 'flush');
$this->redis->script($key1, 'flush');
$this->redis->script($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);
$result = $this->redis->eval($scr, [$key1, $key2], 2);
$this->assertNull($result);
}
public function testEvalBulkEmptyResponseMulti() {
$str_key1 = uniqid() . '-' . rand(1,1000) . '{hash}';
$str_key2 = uniqid() . '-' . rand(1,1000) . '{hash}';
$key1 = uniqid() . '-' . rand(1, 1000) . '{hash}';
$key2 = uniqid() . '-' . rand(1, 1000) . '{hash}';
$this->redis->script($str_key1, 'flush');
$this->redis->script($str_key2, 'flush');
$this->redis->script($key1, 'flush');
$this->redis->script($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);
$this->redis->eval($scr, [$key1, $key2], 2);
$result = $this->redis->exec();
$this->assertNull($result[0]);
@@ -509,84 +557,90 @@ class Redis_Cluster_Test extends Redis_Test {
/* Cluster specific introspection stuff */
public function testIntrospection() {
$arr_masters = $this->redis->_masters();
$this->assertTrue(is_array($arr_masters));
$primaries = $this->redis->_masters();
$this->assertIsArray($primaries);
foreach ($arr_masters as $arr_info) {
$this->assertIsArray($arr_info);
$this->assertIsString($arr_info[0]);
$this->assertIsInt($arr_info[1]);
foreach ($primaries as [$host, $port]) {
$this->assertIsString($host);
$this->assertIsInt($port);
}
}
protected function genKeyName($i_key_idx, $i_type) {
switch ($i_type) {
protected function keyTypeToString($key_type) {
switch ($key_type) {
case Redis::REDIS_STRING:
return "string-$i_key_idx";
return "string";
case Redis::REDIS_SET:
return "set-$i_key_idx";
return "set";
case Redis::REDIS_LIST:
return "list-$i_key_idx";
return "list";
case Redis::REDIS_ZSET:
return "zset-$i_key_idx";
return "zset";
case Redis::REDIS_HASH:
return "hash-$i_key_idx";
return "hash";
case Redis::REDIS_STREAM:
return "stream";
default:
return "unknown-$i_key_idx";
return "unknown($key_type)";
}
}
protected function setKeyVals($i_key_idx, $i_type, &$arr_ref) {
$str_key = $this->genKeyName($i_key_idx, $i_type);
protected function genKeyName($key_index, $key_type) {
return sprintf('%s-%s', $this->keyTypeToString($key_type), $key_index);
}
$this->redis->del($str_key);
protected function setKeyVals($key_index, $key_type, &$arr_ref) {
$key = $this->genKeyName($key_index, $key_type);
switch ($i_type) {
$this->redis->del($key);
switch ($key_type) {
case Redis::REDIS_STRING:
$value = "$str_key-value";
$this->redis->set($str_key, $value);
$value = "$key-value";
$this->redis->set($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'
"$key-mem1", "$key-mem2", "$key-mem3",
"$key-mem4", "$key-mem5", "$key-mem6"
];
$arr_args = $value;
array_unshift($arr_args, $str_key);
call_user_func_array([$this->redis, 'sadd'], $arr_args);
$args = $value;
array_unshift($args, $key);
call_user_func_array([$this->redis, 'sadd'], $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'
"$key-mem1" => "$key-val1",
"$key-mem2" => "$key-val2",
"$key-mem3" => "$key-val3"
];
$this->redis->hmset($str_key, $value);
$this->redis->hmset($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'
"$key-ele1", "$key-ele2", "$key-ele3",
"$key-ele4", "$key-ele5", "$key-ele6"
];
$arr_args = $value;
array_unshift($arr_args, $str_key);
call_user_func_array([$this->redis, 'rpush'], $arr_args);
$args = $value;
array_unshift($args, $key);
call_user_func_array([$this->redis, 'rpush'], $args);
break;
case Redis::REDIS_ZSET:
$i_score = 1;
$score = 1;
$value = [
$str_key . '-mem1' => 1, $str_key . '-mem2' => 2,
$str_key . '-mem3' => 3, $str_key . '-mem3' => 3
"$key-mem1" => 1, "$key-mem2" => 2,
"$key-mem3" => 3, "$key-mem3" => 3
];
foreach ($value as $str_mem => $i_score) {
$this->redis->zadd($str_key, $i_score, $str_mem);
foreach ($value as $mem => $score) {
$this->redis->zadd($key, $score, $mem);
}
break;
}
/* Update our reference array so we can verify values */
$arr_ref[$str_key] = $value;
return $str_key;
$arr_ref[$key] = $value;
return $key;
}
/* Verify that our ZSET values are identical */
@@ -603,51 +657,51 @@ class Redis_Cluster_Test extends Redis_Test {
}
}
protected function checkKeyValue($str_key, $i_type, $value) {
switch ($i_type) {
protected function checkKeyValue($key, $key_type, $value) {
switch ($key_type) {
case Redis::REDIS_STRING:
$this->assertEquals($value, $this->redis->get($str_key));
$this->assertEquals($value, $this->redis->get($key));
break;
case Redis::REDIS_SET:
$arr_r_values = $this->redis->sMembers($str_key);
$arr_r_values = $this->redis->sMembers($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));
$this->assertEquals($value, $this->redis->lrange($key, 0, -1));
break;
case Redis::REDIS_HASH:
$this->assertEquals($value, $this->redis->hgetall($str_key));
$this->assertEquals($value, $this->redis->hgetall($key));
break;
case Redis::REDIS_ZSET:
$this->checkZSetEquality($value, $this->redis->zrange($str_key,0,-1,true));
$this->checkZSetEquality($value, $this->redis->zrange($key, 0, -1, true));
break;
default:
throw new Exception("Unknown type " . $i_type);
throw new Exception("Unknown type " . $key_type);
}
}
/* Test automatic load distributor */
public function testFailOver() {
$arr_value_ref = [];
$arr_type_ref = [];
$value_ref = [];
$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;
foreach ($this->redis_types as $type) {
$key = $this->setKeyVals($i, $type, $value_ref);
$type_ref[$key] = $type;
}
}
/* Iterate over failover options */
foreach ($this->_arr_failover_types as $i_opt) {
$this->redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $i_opt);
foreach ($this->failover_types as $failover_type) {
$this->redis->setOption(RedisCluster::OPT_SLAVE_FAILOVER, $failover_type);
foreach ($arr_value_ref as $str_key => $value) {
$this->checkKeyValue($str_key, $arr_type_ref[$str_key], $value);
foreach ($value_ref as $key => $value) {
$this->checkKeyValue($key, $type_ref[$key], $value);
}
break;
@@ -660,8 +714,8 @@ class Redis_Cluster_Test extends Redis_Test {
$this->assertEquals('my-value', $this->redis->get('mykey'));
$this->redis->del('mylist');
$this->redis->rpush('mylist', 'A','B','C','D');
$this->assertEquals(['A','B','C','D'], $this->redis->lrange('mylist', 0, -1));
$this->redis->rpush('mylist', 'A', 'B', 'C', 'D');
$this->assertEquals(['A', 'B', 'C', 'D'], $this->redis->lrange('mylist', 0, -1));
}
protected function rawCommandArray($key, $args) {
@@ -718,8 +772,8 @@ class Redis_Cluster_Test extends Redis_Test {
$pong = 0;
for ($i = 0; $i < 10; $i++) {
$obj_rc = $this->newInstance();
$pong += $obj_rc->ping("key:$i");
$new_client = $this->newInstance();
$pong += $new_client->ping("key:$i");
}
$this->assertEquals($pong, $i);
@@ -734,8 +788,8 @@ class Redis_Cluster_Test extends Redis_Test {
$pong = 0;
for ($i = 0; $i < 10; $i++) {
$obj_rc = $this->newInstance();
$pong += $obj_rc->ping("key:$i");
$new_client = $this->newInstance();
$pong += $new_client->ping("key:$i");
}
$this->assertEquals($pong, $i);
@@ -756,7 +810,7 @@ class Redis_Cluster_Test extends Redis_Test {
protected function sessionSavePath(): string {
return implode('&', array_map(function ($host) {
return 'seed[]=' . $host;
}, self::$_arr_node_map)) . '&' . $this->getAuthFragment();
}, self::$seeds)) . '&' . $this->getAuthFragment();
}
/* Test correct handling of null multibulk replies */
+449 -484
View File
File diff suppressed because it is too large Load Diff
+41 -30
View File
@@ -24,7 +24,7 @@ function getClassArray($classes) {
}
function getTestClass($class) {
$arr_valid_classes = [
$valid_classes = [
'redis' => 'Redis_Test',
'redisarray' => 'Redis_Array_Test',
'rediscluster' => 'Redis_Cluster_Test',
@@ -32,80 +32,91 @@ function getTestClass($class) {
];
/* Return early if the class is one of our built-in ones */
if (isset($arr_valid_classes[$class]))
return $arr_valid_classes[$class];
if (isset($valid_classes[$class]))
return $valid_classes[$class];
/* Try to load it */
return TestSuite::loadTestClass($class);
}
function raHosts($host, $ports) {
if ( ! is_array($ports))
$ports = [6379, 6380, 6381, 6382];
return array_map(function ($port) use ($host) {
return sprintf("%s:%d", $host, $port);
}, $ports);
}
/* 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:']);
$opt = getopt('', ['host:', 'port:', 'class:', 'test:', 'nocolors', 'user:', 'auth:']);
/* The test class(es) we want to run */
$arr_classes = getClassArray($arr_args['class'] ?? 'redis');
$classes = getClassArray($opt['class'] ?? 'redis');
$boo_colorize = !isset($arr_args['nocolors']);
$colorize = !isset($opt['nocolors']);
/* Get our test filter if provided one */
$str_filter = isset($arr_args['test']) ? $arr_args['test'] : NULL;
$filter = $opt['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;
$host = $opt['host'] ?? '127.0.0.1';
$port = $opt['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;
$user = $opt['user'] ?? NULL;
$auth = $opt['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");
if ($user && $auth) {
$auth = [$user, $auth];
} else if ($user && ! $auth) {
echo TestSuite::make_warning("User passed without a password!\n");
}
/* Toggle colorization in our TestSuite class */
TestSuite::flagColorization($boo_colorize);
TestSuite::flagColorization($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";
echo "Using PHP version " . PHP_VERSION . " (" . (PHP_INT_SIZE * 8) . " bits)\n";
foreach ($arr_classes as $str_class) {
$str_class = getTestClass($str_class);
foreach ($classes as $class) {
$class = getTestClass($class);
/* Depending on the classes being tested, run our tests on it */
echo "Testing class ";
if ($str_class == 'Redis_Array_Test') {
if ($class == 'Redis_Array_Test') {
echo TestSuite::make_bold("RedisArray") . "\n";
foreach(array(true, false) as $useIndex) {
$full_ring = raHosts($host, $port);
$sub_ring = array_slice($full_ring, 0, -1);
echo TestSuite::make_bold("Full Ring: ") . implode(' ', $full_ring) . "\n";
echo TestSuite::make_bold(" New Ring: ") . implode(' ', $sub_ring) . "\n";
foreach([true, false] as $useIndex) {
echo "\n". ($useIndex ? "WITH" : "WITHOUT") . " per-node index:\n";
/* The various RedisArray subtests we can run */
$arr_ra_tests = [
$test_classes = [
'Redis_Array_Test', 'Redis_Rehashing_Test', 'Redis_Auto_Rehashing_Test',
'Redis_Multi_Exec_Test', 'Redis_Distributor_Test'
];
foreach ($arr_ra_tests as $str_test) {
foreach ($test_classes as $test_class) {
/* Run until we encounter a failure */
if (run_tests($str_test, $str_filter, $str_host, $auth) != 0) {
if (run_ra_tests($test_class, $filter, $host, $full_ring, $sub_ring, $auth) != 0) {
exit(1);
}
}
}
} else {
echo TestSuite::make_bold($str_class) . "\n";
if (TestSuite::run("$str_class", $str_filter, $str_host, $i_port, $auth))
echo TestSuite::make_bold($class) . "\n";
if (TestSuite::run("$class", $filter, $host, $port, $auth))
exit(1);
}
}
+38 -16
View File
@@ -187,6 +187,15 @@ class TestSuite
return false;
}
protected function assertIsBool($v): bool {
if (is_bool($v))
return true;
self::$errors []= $this->assertionTrace("%s is not a boolean", $this->printArg($v));
return false;
}
protected function assertIsInt($v): bool {
if (is_int($v))
return true;
@@ -196,6 +205,19 @@ class TestSuite
return false;
}
protected function assertIsObject($v, ?string $type = NULL): bool {
if ( ! is_object($v)) {
self::$errors []= $this->assertionTrace("%s is not an object", $this->printArg($v));
return false;
} else if ( $type !== NULL && !($v InstanceOf $type)) {
self::$errors []= $this->assertionTrace("%s is not an instance of %s",
$this->printArg($v), $type);
return false;
}
return true;
}
protected function assertIsArray($v, ?int $size = null): bool {
if ( ! is_array($v)) {
self::$errors []= $this->assertionTrace("%s is not an array", $this->printArg($v));
@@ -497,17 +519,17 @@ class TestSuite
posix_isatty(STDOUT);
}
public static function run($className, ?string $limit = NULL,
public static function run($class_name, ?string $limit = NULL,
?string $host = NULL, ?int $port = NULL,
$auth = NULL)
{
/* Lowercase our limit arg if we're passed one */
$limit ??= strtolower($limit);
$rc = new ReflectionClass($className);
$rc = new ReflectionClass($class_name);
$methods = $rc->GetMethods(ReflectionMethod::IS_PUBLIC);
$i_max_len = self::getMaxTestLen($methods, $limit);
$max_test_len = self::getMaxTestLen($methods, $limit);
foreach($methods as $m) {
$name = $m->name;
@@ -520,42 +542,42 @@ class TestSuite
continue;
}
$str_out_name = str_pad($name, $i_max_len + 1);
$str_out_name = str_pad($name, $max_test_len + 1);
echo self::make_bold($str_out_name);
$count = count($className::$errors);
$rt = new $className($host, $port, $auth);
$count = count($class_name::$errors);
$rt = new $class_name($host, $port, $auth);
try {
$rt->setUp();
$rt->$name();
if ($count === count($className::$errors)) {
$str_msg = self::make_success('PASSED');
if ($count === count($class_name::$errors)) {
$result = self::make_success('PASSED');
} else {
$str_msg = self::make_fail('FAILED');
$result = self::make_fail('FAILED');
}
} catch (Exception $e) {
/* We may have simply skipped the test */
if ($e instanceof TestSkippedException) {
$str_msg = self::make_warning('SKIPPED');
$result = self::make_warning('SKIPPED');
} else {
$className::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n";
$str_msg = self::make_fail('FAILED');
$class_name::$errors[] = "Uncaught exception '".$e->getMessage()."' ($name)\n";
$result = self::make_fail('FAILED');
}
}
echo "[" . $str_msg . "]\n";
echo "[" . $result . "]\n";
}
echo "\n";
echo implode('', $className::$warnings) . "\n";
echo implode('', $class_name::$warnings) . "\n";
if (empty($className::$errors)) {
if (empty($class_name::$errors)) {
echo "All tests passed. \o/\n";
return 0;
}
echo implode('', $className::$errors);
echo implode('', $class_name::$errors);
return 1;
}
}