first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,193 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace cachestore_redis;
use cache_definition;
use cache_store;
use cachestore_redis;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/../../../tests/fixtures/stores.php');
require_once(__DIR__ . '/../lib.php');
/**
* Redis cluster test.
*
* If you wish to use these unit tests all you need to do is add the following definition to
* your config.php file:
*
* define('TEST_CACHESTORE_REDIS_SERVERSCLUSTER', 'localhost:7000,localhost:7001');
* define('TEST_CACHESTORE_REDIS_ENCRYPTCLUSTER', true);
* define('TEST_CACHESTORE_REDIS_AUTHCLUSTER', 'foobared');
* define('TEST_CACHESTORE_REDIS_CASCLUSTER', '/cafile/dir/ca.crt');
*
* @package cachestore_redis
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2017 Catalyst IT Australia {@link http://www.catalyst-au.net}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*
* @coversDefaultClass \cachestore_redis
*/
class cachestore_cluster_redis_test extends \advanced_testcase {
/**
* Create a cache store for testing the Redis cluster.
*
* @param string|null $seed The redis cluster servers.
* @return cachestore_redis The created cache store instance.
*/
public function create_store(?string $seed = null): cachestore_redis {
global $DB;
$definition = cache_definition::load_adhoc(
mode: cache_store::MODE_APPLICATION,
component: 'cachestore_redis',
area: 'phpunit_test',
);
$servers = $seed ?? str_replace(",", "\n", TEST_CACHESTORE_REDIS_SERVERSCLUSTER);
$config = [
'server' => $servers,
'prefix' => $DB->get_prefix(),
'clustermode' => true,
];
if (defined('TEST_CACHESTORE_REDIS_ENCRYPTCLUSTER') && TEST_CACHESTORE_REDIS_ENCRYPTCLUSTER === true) {
$config['encryption'] = true;
}
if (defined('TEST_CACHESTORE_REDIS_AUTHCLUSTER') && TEST_CACHESTORE_REDIS_AUTHCLUSTER) {
$config['password'] = TEST_CACHESTORE_REDIS_AUTHCLUSTER;
}
if (defined('TEST_CACHESTORE_REDIS_CASCLUSTER') && TEST_CACHESTORE_REDIS_CASCLUSTER) {
$config['cafile'] = TEST_CACHESTORE_REDIS_CASCLUSTER;
}
$store = new cachestore_redis('TestCluster', $config);
$store->initialise($definition);
$store->purge();
return $store;
}
/**
* Set up the test environment.
*/
public function setUp(): void {
if (!cachestore_redis::are_requirements_met()) {
$this->markTestSkipped('Could not test cachestore_redis with cluster, missing requirements.');
} else if (!\cache_helper::is_cluster_available()) {
$this->markTestSkipped('Could not test cachestore_redis with cluster, class RedisCluster is not available.');
} else if (!defined('TEST_CACHESTORE_REDIS_SERVERSCLUSTER')) {
$this->markTestSkipped('Could not test cachestore_redis with cluster, missing configuration. ' .
"Example: define('TEST_CACHESTORE_REDIS_SERVERSCLUSTER', " .
"'localhost:7000,localhost:7001,localhost:7002');");
}
}
/**
* Test if the cache store can be created successfully.
*
* @covers ::is_ready
*/
public function test_it_can_create(): void {
$store = $this->create_store();
$this->assertNotNull($store);
$this->assertTrue($store->is_ready());
}
/**
* Test if the cache store trims server names correctly.
*
* @covers ::new_redis
*/
public function test_it_trims_server_names(): void {
// Add a time before and spaces after the first server. Also adds a blank line before second server.
$servers = explode(',', TEST_CACHESTORE_REDIS_SERVERSCLUSTER);
$servers[0] = "\t" . $servers[0] . " \n";
$servers = implode("\n", $servers);
$store = $this->create_store($servers);
$this->assertTrue($store->is_ready());
}
/**
* Test if the cache store can successfully set and get a value.
*
* @covers ::set
* @covers ::get
*/
public function test_it_can_setget(): void {
$store = $this->create_store();
$store->set('the key', 'the value');
$actual = $store->get('the key');
$this->assertSame('the value', $actual);
}
/**
* Test if the cache store can successfully set and get multiple values.
*
* @covers ::set_many
* @covers ::get_many
*/
public function test_it_can_setget_many(): void {
$store = $this->create_store();
// Create values.
$values = [];
$keys = [];
$expected = [];
for ($i = 0; $i < 10; $i++) {
$key = "getkey_{$i}";
$value = "getvalue #{$i}";
$keys[] = $key;
$values[] = [
'key' => $key,
'value' => $value,
];
$expected[$key] = $value;
}
$store->set_many($values);
$actual = $store->get_many($keys);
$this->assertSame($expected, $actual);
}
/**
* Test if the cache store is marked as not ready if it fails to connect.
*
* @covers ::is_ready
*/
public function test_it_is_marked_not_ready_if_failed_to_connect(): void {
global $DB;
$config = [
'server' => "abc:123",
'prefix' => $DB->get_prefix(),
'clustermode' => true,
];
$store = new cachestore_redis('TestCluster', $config);
$debugging = $this->getDebuggingMessages();
// Failed to connect should show a debugging message.
$this->assertCount(1, \phpunit_util::get_debugging_messages() );
$this->assertStringContainsString('Couldn\'t map cluster keyspace using any provided seed', $debugging[0]->message);
$this->resetDebugging();
$this->assertFalse($store->is_ready());
}
}
+154
View File
@@ -0,0 +1,154 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace cachestore_redis;
use cache_definition;
use cache_store;
use cachestore_redis;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../../../tests/fixtures/stores.php');
require_once(__DIR__.'/../lib.php');
/**
* Redis cache store test.
*
* If you wish to use these unit tests all you need to do is add the following definition to
* your config.php file.
*
* define('TEST_CACHESTORE_REDIS_TESTSERVERS', '127.0.0.1');
*
* @package cachestore_redis
* @copyright (c) 2015 Moodlerooms Inc. (http://www.moodlerooms.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*
* @coversDefaultClass \cachestore_redis
*/
class cachestore_redis_test extends \cachestore_tests {
/** @var cachestore_redis $store Redis Cache Store. */
protected $store;
/**
* Returns the class name.
*
* @return string
*/
protected function get_class_name(): string {
return 'cachestore_redis';
}
public function setUp(): void {
if (!cachestore_redis::are_requirements_met() || !defined('TEST_CACHESTORE_REDIS_TESTSERVERS')) {
$this->markTestSkipped('Could not test cachestore_redis. Requirements are not met.');
}
parent::setUp();
}
protected function tearDown(): void {
parent::tearDown();
if ($this->store instanceof cachestore_redis) {
$this->store->purge();
}
}
/**
* Creates the required cachestore for the tests to run against Redis.
*
* @return cachestore_redis
*/
protected function create_cachestore_redis(): cachestore_redis {
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_redis', 'phpunit_test');
$store = new cachestore_redis('Test', cachestore_redis::unit_test_configuration());
$store->initialise($definition);
$this->store = $store;
$store->purge();
return $store;
}
/**
* Test methods for various operations (set and has) in the cachestore_redis class.
*
* @covers ::set
* @covers ::has
*/
public function test_has(): void {
$store = $this->create_cachestore_redis();
$this->assertTrue($store->set('foo', 'bar'));
$this->assertTrue($store->has('foo'));
$this->assertFalse($store->has('bat'));
}
/**
* Test methods for the 'has_any' operation in the cachestore_redis class.
*
* @covers ::set
* @covers ::has_any
*/
public function test_has_any(): void {
$store = $this->create_cachestore_redis();
$this->assertTrue($store->set('foo', 'bar'));
$this->assertTrue($store->has_any(['bat', 'foo']));
$this->assertFalse($store->has_any(['bat', 'baz']));
}
/**
* PHPUnit test methods for the 'has_all' operation in the cachestore_redis class.
*
* @covers ::set
* @covers ::has_all
*/
public function test_has_all(): void {
$store = $this->create_cachestore_redis();
$this->assertTrue($store->set('foo', 'bar'));
$this->assertTrue($store->set('bat', 'baz'));
$this->assertTrue($store->has_all(['foo', 'bat']));
$this->assertFalse($store->has_all(['foo', 'bat', 'this']));
}
/**
* Test methods for the 'lock' operations in the cachestore_redis class.
*
* @covers ::acquire_lock
* @covers ::check_lock_state
* @covers ::release_lock
*/
public function test_lock(): void {
$store = $this->create_cachestore_redis();
$this->assertTrue($store->acquire_lock('lock', '123'));
$this->assertTrue($store->check_lock_state('lock', '123'));
$this->assertFalse($store->check_lock_state('lock', '321'));
$this->assertNull($store->check_lock_state('notalock', '123'));
$this->assertFalse($store->release_lock('lock', '321'));
$this->assertTrue($store->release_lock('lock', '123'));
}
/**
* Test method to check if the cachestore_redis instance is ready after connecting.
*
* @covers ::is_ready
*/
public function test_it_is_ready_after_connecting(): void {
$store = $this->create_cachestore_redis();
$this::assertTrue($store->is_ready());
}
}
+288
View File
@@ -0,0 +1,288 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace cachestore_redis;
use cache_definition;
use cache_store;
use cachestore_redis;
require_once(__DIR__.'/../../../tests/fixtures/stores.php');
require_once(__DIR__.'/../lib.php');
/**
* Redis cache test - compressor settings.
*
* If you wish to use these unit tests all you need to do is add the following definition to
* your config.php file.
*
* define('TEST_CACHESTORE_REDIS_TESTSERVERS', '127.0.0.1');
*
* @package cachestore_redis
* @author Daniel Thee Roperto <daniel.roperto@catalyst-au.net>
* @copyright 2018 Catalyst IT Australia {@link http://www.catalyst-au.net}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class compressor_test extends \advanced_testcase {
/**
* Test set up
*/
public function setUp(): void {
if (!cachestore_redis::are_requirements_met() || !defined('TEST_CACHESTORE_REDIS_TESTSERVERS')) {
$this->markTestSkipped('Could not test cachestore_redis. Requirements are not met.');
}
parent::setUp();
}
/**
* Create a cachestore.
*
* @param int $compressor
* @param int $serializer
* @return cachestore_redis
*/
public function create_store($compressor, $serializer) {
/** @var cache_definition $definition */
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_redis', 'phpunit_test');
$config = cachestore_redis::unit_test_configuration();
$config['compressor'] = $compressor;
$config['serializer'] = $serializer;
$store = new cachestore_redis('Test', $config);
$store->initialise($definition);
return $store;
}
/**
* It misses a value.
*/
public function test_it_can_miss_one(): void {
$store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, \Redis::SERIALIZER_PHP);
self::assertFalse($store->get('missme'));
}
/**
* It misses many values.
*/
public function test_it_can_miss_many(): void {
$store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, \Redis::SERIALIZER_PHP);
$expected = ['missme' => false, 'missmetoo' => false];
$actual = $store->get_many(array_keys($expected));
self::assertSame($expected, $actual);
}
/**
* It misses some values.
*/
public function test_it_can_miss_some(): void {
$store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, \Redis::SERIALIZER_PHP);
$store->set('iamhere', 'youfoundme');
$expected = ['missme' => false, 'missmetoo' => false, 'iamhere' => 'youfoundme'];
$actual = $store->get_many(array_keys($expected));
self::assertSame($expected, $actual);
}
/**
* A provider for test_works_with_different_types
*
* @return array
*/
public function provider_for_test_it_works_with_different_types() {
$object = new \stdClass();
$object->field = 'value';
return [
['string', 'Abc Def'],
['string_empty', ''],
['string_binary', gzencode('some binary data')],
['int', 123],
['int_zero', 0],
['int_negative', -100],
['int_huge', PHP_INT_MAX],
['float', 3.14],
['boolean_true', true],
// Boolean 'false' is not tested as it is not allowed in Moodle.
['array', [1, 'b', 3.4]],
['array_map', ['a' => 'b', 'c' => 'd']],
['object_stdClass', $object],
['null', null],
];
}
/**
* It works with different types.
*
* @dataProvider provider_for_test_it_works_with_different_types
* @param string $key
* @param mixed $value
*/
public function test_it_works_with_different_types($key, $value): void {
$store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, \Redis::SERIALIZER_PHP);
$store->set($key, $value);
self::assertEquals($value, $store->get($key), "Failed set/get for: {$key}");
}
/**
* Test it works with different types for many.
*/
public function test_it_works_with_different_types_for_many(): void {
$store = $this->create_store(cachestore_redis::COMPRESSOR_PHP_GZIP, \Redis::SERIALIZER_PHP);
$provider = $this->provider_for_test_it_works_with_different_types();
$keys = [];
$values = [];
$expected = [];
foreach ($provider as $item) {
$keys[] = $item[0];
$values[] = ['key' => $item[0], 'value' => $item[1]];
$expected[$item[0]] = $item[1];
}
$store->set_many($values);
$actual = $store->get_many($keys);
self::assertEquals($expected, $actual);
}
/**
* Provider for set/get combination tests.
*
* @return array
*/
public function provider_for_tests_setget() {
if (!cachestore_redis::are_requirements_met()) {
// Even though we skip all tests in this case, this provider can still show warnings about non-existing class.
return [];
}
$data = [
['none, none',
\Redis::SERIALIZER_NONE, cachestore_redis::COMPRESSOR_NONE,
'value1', 'value2'],
['none, gzip',
\Redis::SERIALIZER_NONE, cachestore_redis::COMPRESSOR_PHP_GZIP,
gzencode('value1'), gzencode('value2')],
['php, none',
\Redis::SERIALIZER_PHP, cachestore_redis::COMPRESSOR_NONE,
serialize('value1'), serialize('value2')],
['php, gzip',
\Redis::SERIALIZER_PHP, cachestore_redis::COMPRESSOR_PHP_GZIP,
gzencode(serialize('value1')), gzencode(serialize('value2'))],
];
if (defined('Redis::SERIALIZER_IGBINARY')) {
$data[] = [
'igbinary, none',
\Redis::SERIALIZER_IGBINARY, cachestore_redis::COMPRESSOR_NONE,
igbinary_serialize('value1'), igbinary_serialize('value2'),
];
$data[] = [
'igbinary, gzip',
\Redis::SERIALIZER_IGBINARY, cachestore_redis::COMPRESSOR_PHP_GZIP,
gzencode(igbinary_serialize('value1')), gzencode(igbinary_serialize('value2')),
];
}
if (extension_loaded('zstd')) {
$data[] = [
'none, zstd',
\Redis::SERIALIZER_NONE, cachestore_redis::COMPRESSOR_PHP_ZSTD,
zstd_compress('value1'), zstd_compress('value2'),
];
$data[] = [
'php, zstd',
\Redis::SERIALIZER_PHP, cachestore_redis::COMPRESSOR_PHP_ZSTD,
zstd_compress(serialize('value1')), zstd_compress(serialize('value2')),
];
if (defined('\Redis::SERIALIZER_IGBINARY')) {
$data[] = [
'igbinary, zstd',
\Redis::SERIALIZER_IGBINARY, cachestore_redis::COMPRESSOR_PHP_ZSTD,
zstd_compress(igbinary_serialize('value1')), zstd_compress(igbinary_serialize('value2')),
];
}
}
return $data;
}
/**
* Test we can use get and set with all combinations.
*
* @dataProvider provider_for_tests_setget
* @param string $name
* @param int $serializer
* @param int $compressor
* @param string $rawexpected1
* @param string $rawexpected2
*/
public function test_it_can_use_getset($name, $serializer, $compressor, $rawexpected1, $rawexpected2): void {
// Create a connection with the desired serialisation.
$store = $this->create_store($compressor, $serializer);
$store->set('key', 'value1');
// Disable compressor and serializer to check the actual stored value.
$rawstore = $this->create_store(cachestore_redis::COMPRESSOR_NONE, \Redis::SERIALIZER_NONE);
$data = $store->get('key');
$rawdata = $rawstore->get('key');
self::assertSame('value1', $data, "Invalid serialisation/unserialisation for: {$name}");
self::assertSame($rawexpected1, $rawdata, "Invalid rawdata for: {$name}");
}
/**
* Test we can use get and set many with all combinations.
*
* @dataProvider provider_for_tests_setget
* @param string $name
* @param int $serializer
* @param int $compressor
* @param string $rawexpected1
* @param string $rawexpected2
*/
public function test_it_can_use_getsetmany($name, $serializer, $compressor, $rawexpected1, $rawexpected2): void {
$many = [
['key' => 'key1', 'value' => 'value1'],
['key' => 'key2', 'value' => 'value2'],
];
$keys = ['key1', 'key2'];
$expectations = ['key1' => 'value1', 'key2' => 'value2'];
$rawexpectations = ['key1' => $rawexpected1, 'key2' => $rawexpected2];
// Create a connection with the desired serialisation.
$store = $this->create_store($compressor, $serializer);
$store->set_many($many);
// Disable compressor and serializer to check the actual stored value.
$rawstore = $this->create_store(cachestore_redis::COMPRESSOR_NONE, \Redis::SERIALIZER_NONE);
$data = $store->get_many($keys);
$rawdata = $rawstore->get_many($keys);
foreach ($keys as $key) {
self::assertSame($expectations[$key],
$data[$key],
"Invalid serialisation/unserialisation for {$key} with serializer {$name}");
self::assertSame($rawexpectations[$key],
$rawdata[$key],
"Invalid rawdata for {$key} with serializer {$name}");
}
}
}
+275
View File
@@ -0,0 +1,275 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace cachestore_redis;
use cache_store;
use cache_definition;
use cachestore_redis;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__.'/../../../tests/fixtures/stores.php');
require_once(__DIR__.'/../lib.php');
/**
* Redis cache test.
*
* If you wish to use these unit tests all you need to do is add the following definition to
* your config.php file.
*
* define('TEST_CACHESTORE_REDIS_TESTSERVERS', '127.0.0.1');
*
* @package cachestore_redis
* @covers \cachestore_redis
* @copyright Copyright (c) 2015 Moodlerooms Inc. (http://www.moodlerooms.com)
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class store_test extends \cachestore_tests {
/**
* @var cachestore_redis
*/
protected $store;
/**
* Returns the MongoDB class name
*
* @return string
*/
protected function get_class_name() {
return 'cachestore_redis';
}
public function setUp(): void {
if (!cachestore_redis::are_requirements_met() || !defined('TEST_CACHESTORE_REDIS_TESTSERVERS')) {
$this->markTestSkipped('Could not test cachestore_redis. Requirements are not met.');
}
parent::setUp();
}
protected function tearDown(): void {
parent::tearDown();
if ($this->store instanceof cachestore_redis) {
$this->store->purge();
}
}
/**
* Creates the required cachestore for the tests to run against Redis.
*
* @param array $extraconfig Extra configuration options for Redis instance, if any
* @param bool $ttl True to use a cache definition with TTL enabled
* @return cachestore_redis
*/
protected function create_cachestore_redis(array $extraconfig = [], bool $ttl = false): cachestore_redis {
if ($ttl) {
/** @var cache_definition $definition */
$definition = cache_definition::load('core/wibble', [
'mode' => 1,
'simplekeys' => true,
'simpledata' => true,
'ttl' => 10,
'component' => 'core',
'area' => 'wibble',
'selectedsharingoption' => 2,
'userinputsharingkey' => '',
'sharingoptions' => 15,
]);
} else {
/** @var cache_definition $definition */
$definition = cache_definition::load_adhoc(cache_store::MODE_APPLICATION, 'cachestore_redis', 'phpunit_test');
}
$configuration = array_merge(cachestore_redis::unit_test_configuration(), $extraconfig);
$store = new cachestore_redis('Test', $configuration);
$store->initialise($definition);
$this->store = $store;
if (!$store) {
$this->markTestSkipped();
}
return $store;
}
public function test_has(): void {
$store = $this->create_cachestore_redis();
$this->assertTrue($store->set('foo', 'bar'));
$this->assertTrue($store->has('foo'));
$this->assertFalse($store->has('bat'));
}
public function test_has_any(): void {
$store = $this->create_cachestore_redis();
$this->assertTrue($store->set('foo', 'bar'));
$this->assertTrue($store->has_any(array('bat', 'foo')));
$this->assertFalse($store->has_any(array('bat', 'baz')));
}
public function test_has_all(): void {
$store = $this->create_cachestore_redis();
$this->assertTrue($store->set('foo', 'bar'));
$this->assertTrue($store->set('bat', 'baz'));
$this->assertTrue($store->has_all(array('foo', 'bat')));
$this->assertFalse($store->has_all(array('foo', 'bat', 'this')));
}
public function test_lock(): void {
$store = $this->create_cachestore_redis();
$this->assertTrue($store->acquire_lock('lock', '123'));
$this->assertTrue($store->check_lock_state('lock', '123'));
$this->assertFalse($store->check_lock_state('lock', '321'));
$this->assertNull($store->check_lock_state('notalock', '123'));
$this->assertFalse($store->release_lock('lock', '321'));
$this->assertTrue($store->release_lock('lock', '123'));
}
/**
* Checks the timeout features of locking.
*/
public function test_lock_timeouts(): void {
$store = $this->create_cachestore_redis(['lockwait' => 2, 'locktimeout' => 4]);
// User 123 acquires lock.
$this->assertTrue($store->acquire_lock('lock', '123'));
$this->assertTrue($store->check_lock_state('lock', '123'));
// User 456 tries to acquire lock - should fail after about 2 seconds.
$before = microtime(true);
$this->assertFalse($store->acquire_lock('lock', '456'));
$after = microtime(true);
$this->assertEqualsWithDelta(2, $after - $before, 0.5);
// Wait another 2 seconds and then it should be able to get the lock because of timeout.
sleep(2);
$this->assertTrue($store->acquire_lock('lock', '456'));
$this->assertTrue($store->check_lock_state('lock', '456'));
// The first user doesn't have the lock any more.
$this->assertFalse($store->check_lock_state('lock', '123'));
// Releasing the lock from the first user does nothing.
$this->assertFalse($store->release_lock('lock', '123'));
$this->assertTrue($store->check_lock_state('lock', '456'));
$this->assertTrue($store->release_lock('lock', '456'));
}
/**
* Tests the shutdown function that is supposed to free any remaining locks.
*/
public function test_lock_shutdown(): void {
$store = $this->create_cachestore_redis();
try {
$this->assertTrue($store->acquire_lock('a', '123'));
$this->assertTrue($store->acquire_lock('b', '123'));
$this->assertTrue($store->acquire_lock('c', '123'));
$this->assertTrue($store->check_lock_state('a', '123'));
$this->assertTrue($store->check_lock_state('b', '123'));
$this->assertTrue($store->check_lock_state('c', '123'));
} finally {
$store->shutdown_release_locks();
$this->assertDebuggingCalledCount(3);
}
$this->assertNull($store->check_lock_state('a', '123'));
$this->assertNull($store->check_lock_state('b', '123'));
$this->assertNull($store->check_lock_state('c', '123'));
}
/**
* Tests the get_last_io_bytes function when not using compression (just returns unknown).
*/
public function test_get_last_io_bytes(): void {
$store = $this->create_cachestore_redis();
$store->set('foo', [1, 2, 3, 4]);
$this->assertEquals(\cache_store::IO_BYTES_NOT_SUPPORTED, $store->get_last_io_bytes());
$store->get('foo');
$this->assertEquals(\cache_store::IO_BYTES_NOT_SUPPORTED, $store->get_last_io_bytes());
}
/**
* Tests the get_last_io_bytes byte count when using compression.
*/
public function test_get_last_io_bytes_compressed(): void {
$store = $this->create_cachestore_redis(['compressor' => cachestore_redis::COMPRESSOR_PHP_GZIP]);
$alphabet = 'abcdefghijklmnopqrstuvwxyz';
$store->set('small', $alphabet);
$store->set('large', str_repeat($alphabet, 10));
$store->get('small');
// Interesting 'compression'.
$this->assertEquals(54, $store->get_last_io_bytes());
$store->get('large');
// This one is actually smaller than uncompressed value!
$this->assertEquals(57, $store->get_last_io_bytes());
$store->get_many(['small', 'large']);
$this->assertEquals(111, $store->get_last_io_bytes());
$store->set('small', str_repeat($alphabet, 2));
$this->assertEquals(56, $store->get_last_io_bytes());
$store->set_many([
['key' => 'small', 'value' => $alphabet],
['key' => 'large', 'value' => str_repeat($alphabet, 10)]
]);
$this->assertEquals(111, $store->get_last_io_bytes());
}
/**
* Data provider for whether cache uses TTL or not.
*
* @return array Array with true and false options
*/
public static function ttl_or_not(): array {
return [
[false],
[true]
];
}
/**
* Tests the delete_many function.
*
* The behaviour is different with TTL enabled so we need to test with that kind of definition
* as well as a 'normal' one.
*
* @param bool $ttl True to test using a TTL definition
* @dataProvider ttl_or_not
*/
public function test_delete_many(bool $ttl): void {
$store = $this->create_cachestore_redis([], $ttl);
// Check it works to delete selected items.
$store->set('foo', 'frog');
$store->set('bar', 'amphibian');
$store->set('hmm', 'undead');
$this->store->delete_many(['foo', 'bar']);
$this->assertFalse($store->get('foo'));
$this->assertFalse($store->get('bar'));
$this->assertEquals('undead', $store->get('hmm'));
// If called with no keys it should do nothing.
$store->delete_many([]);
$this->assertEquals('undead', $store->get('hmm'));
}
}
+122
View File
@@ -0,0 +1,122 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace cachestore_redis;
/**
* TTL support test for Redis cache.
*
* If you wish to use these unit tests all you need to do is add the following definition to
* your config.php file.
*
* define('TEST_CACHESTORE_REDIS_TESTSERVERS', '127.0.0.1');
*
* @package cachestore_redis
* @copyright 2021 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \cachestore_redis
*/
final class ttl_test extends \advanced_testcase {
/** @var \cachestore_redis|null Cache store */
protected $store = null;
public function setUp(): void {
// Make sure cachestore_redis is available.
require_once(__DIR__ . '/../lib.php');
if (!\cachestore_redis::are_requirements_met() || !defined('TEST_CACHESTORE_REDIS_TESTSERVERS')) {
$this->markTestSkipped('Could not test cachestore_redis. Requirements are not met.');
}
// Set up a Redis store with a fake definition that has TTL set to 10 seconds.
$definition = \cache_definition::load('core/wibble', [
'mode' => 1,
'simplekeys' => true,
'simpledata' => true,
'ttl' => 10,
'component' => 'core',
'area' => 'wibble',
'selectedsharingoption' => 2,
'userinputsharingkey' => '',
'sharingoptions' => 15,
]);
$this->store = new \cachestore_redis('Test', \cachestore_redis::unit_test_configuration());
$this->store->initialise($definition);
parent::setUp();
}
protected function tearDown(): void {
parent::tearDown();
if ($this->store instanceof \cachestore_redis) {
$this->store->purge();
}
}
/**
* Test calling set_many with an empty array
*
* Trivial test to ensure we don't trigger an ArgumentCountError when calling zAdd with invalid parameters
*/
public function test_set_many_empty(): void {
$this->assertEquals(0, $this->store->set_many([]));
}
/**
* Tests expiring data.
*/
public function test_expire_ttl(): void {
$this->resetAfterTest();
// Set some data at time 100.
\cachestore_redis::set_phpunit_time(100);
$this->store->set('a', 1);
$this->store->set('b', 2);
$this->store->set_many([['key' => 'c', 'value' => 3], ['key' => 'd', 'value' => 4],
['key' => 'e', 'value' => 5], ['key' => 'f', 'value' => 6],
['key' => 'g', 'value' => 7], ['key' => 'h', 'value' => 8]]);
// Set some other data at time 110, including some of the existing values. Whether the
// value changes or not, its TTL should update.
\cachestore_redis::set_phpunit_time(110);
$this->store->set('b', 2);
$this->store->set_many([['key' => 'c', 'value' => 99], ['key' => 'd', 'value' => 4]]);
// Check all the data is still set.
$this->assertEqualsCanonicalizing(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
$this->store->find_all());
// Delete some data (to check deletion doesn't confuse expiry).
$this->store->delete('f');
$this->store->delete_many(['g', 'h']);
// Set time to 115 and expire data.
\cachestore_redis::set_phpunit_time(115);
$info = $this->store->expire_ttl();
// We are expecting keys a and e to be deleted.
$this->assertEquals(2, $info['keys']);
$this->assertEquals(1, $info['batches']);
// Check the keys are as expected.
$this->assertEqualsCanonicalizing(['b', 'c', 'd'], $this->store->find_all());
// Might as well check the values of the surviving keys.
$this->assertEquals(2, $this->store->get('b'));
$this->assertEquals(99, $this->store->get('c'));
$this->assertEquals(4, $this->store->get('d'));
}
}