See Release Notes
Long Term Support Release
<?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 core_cache; use cache; use cache_application; use cache_config; use cache_config_disabled; use cache_config_testing; use cache_definition; use cache_disabled; use cache_factory; use cache_factory_disabled; use cache_helper; use cache_loader; use cache_phpunit_application; use cache_phpunit_cache; use cache_phpunit_dummy_object; use cache_phpunit_dummy_overrideclass; use cache_phpunit_factory; use cache_phpunit_request; use cache_phpunit_session; use cache_request; use cache_session; use cache_store; use cacheable_object_array;< defined('MOODLE_INTERNAL') || die(); < < // Include the necessary evils. < global $CFG; < require_once($CFG->dirroot.'/cache/locallib.php'); < require_once($CFG->dirroot.'/cache/tests/fixtures/lib.php'); </** * PHPunit tests for the cache API * * This file is part of Moodle's cache API, affectionately called MUC. * It contains the components that are requried in order to use caching. * * @package core * @category cache * @copyright 2012 Sam Hemelryk * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later> * @coversDefaultClass \cache */ > * @covers \cacheclass cache_test extends \advanced_testcase { /**> * Load required libraries and fixtures. * Set things back to the default before each test. > */ */ > public static function setUpBeforeClass(): void { public function setUp(): void { > global $CFG; parent::setUp(); > cache_factory::reset(); > require_once($CFG->dirroot . '/cache/locallib.php'); cache_config_testing::create_default_configuration(); > require_once($CFG->dirroot . '/cache/tests/fixtures/lib.php'); } > require_once($CFG->dirroot . '/cache/tests/fixtures/cache_phpunit_dummy_datasource_versionable.php'); > } /** > * Final task is to reset the cache system > /***/ public static function tearDownAfterClass(): void { parent::tearDownAfterClass(); cache_factory::reset(); } /** * Returns the expected application cache store. * @return string */ protected function get_expected_application_cache_store() { global $CFG; $expected = 'cachestore_file'; // Verify if we are using any of the available ways to use a different application store within tests. if (defined('TEST_CACHE_USING_APPLICATION_STORE') && preg_match('#[a-zA-Z][a-zA-Z0-9_]*#', TEST_CACHE_USING_APPLICATION_STORE)) { // 1st way. Using some of the testing servers. $expected = 'cachestore_'.(string)TEST_CACHE_USING_APPLICATION_STORE; } else if (defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH && !empty($CFG->altcacheconfigpath)) { // 2nd way. Using an alternative configuration. $defaultstores = cache_helper::get_stores_suitable_for_mode_default(); $instance = cache_config::instance(); // Iterate over defined mode mappings until we get an application one not being the default. foreach ($instance->get_mode_mappings() as $mapping) { // If the store is not for application mode, ignore. if ($mapping['mode'] !== cache_store::MODE_APPLICATION) { continue; } // If the store matches some default mapping store name, ignore. if (array_key_exists($mapping['store'], $defaultstores) && !empty($defaultstores[$mapping['store']]['default'])) { continue; } // Arrived here, have found an application mode store not being the default mapped one (file), // that's the one we are using in the configuration for sure. $expected = 'cachestore_'.$mapping['store']; } } return $expected; } /** * Tests cache configuration */ public function test_cache_config() { global $CFG; if (defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH && !empty($CFG->altcacheconfigpath)) { // We need to skip this test - it checks the default config structure, but very likely we arn't using the // default config structure here so theres no point in running the test. $this->markTestSkipped('Skipped testing default cache config structure as alt cache path is being used.'); } if (defined('TEST_CACHE_USING_APPLICATION_STORE')) { // We need to skip this test - it checks the default config structure, but very likely we arn't using the // default config structure here because we are testing against an alternative application store. $this->markTestSkipped('Skipped testing default cache config structure as alt application store is being used.'); } $instance = cache_config::instance(); $this->assertInstanceOf(cache_config_testing::class, $instance); $this->assertTrue(cache_config_testing::config_file_exists()); $stores = $instance->get_all_stores(); $this->assertCount(3, $stores); foreach ($stores as $name => $store) { // Check its an array. $this->assertIsArray($store); // Check the name is the key. $this->assertEquals($name, $store['name']); // Check that it has been declared default. $this->assertTrue($store['default']); // Required attributes = name + plugin + configuration + modes + features. $this->assertArrayHasKey('name', $store); $this->assertArrayHasKey('plugin', $store); $this->assertArrayHasKey('configuration', $store); $this->assertArrayHasKey('modes', $store); $this->assertArrayHasKey('features', $store); } $modemappings = $instance->get_mode_mappings(); $this->assertCount(3, $modemappings); $modes = array( cache_store::MODE_APPLICATION => false, cache_store::MODE_SESSION => false, cache_store::MODE_REQUEST => false, ); foreach ($modemappings as $mapping) { // We expect 3 properties. $this->assertCount(3, $mapping); // Required attributes = mode + store. $this->assertArrayHasKey('mode', $mapping); $this->assertArrayHasKey('store', $mapping); // Record the mode. $modes[$mapping['mode']] = true; } // Must have the default 3 modes and no more. $this->assertCount(3, $mapping); foreach ($modes as $mode) { $this->assertTrue($mode); } $definitions = $instance->get_definitions(); // The event invalidation definition is required for the cache API and must be there. $this->assertArrayHasKey('core/eventinvalidation', $definitions); $definitionmappings = $instance->get_definition_mappings(); foreach ($definitionmappings as $mapping) { // Required attributes = definition + store. $this->assertArrayHasKey('definition', $mapping); $this->assertArrayHasKey('store', $mapping); } } /** * Tests for cache keys that would break on windows. */ public function test_windows_nasty_keys() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/windowskeytest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'windowskeytest', 'simplekeys' => true, 'simpledata' => true )); $cache = cache::make('phpunit', 'windowskeytest'); $this->assertTrue($cache->set('contest', 'test data 1')); $this->assertEquals('test data 1', $cache->get('contest')); } /** * Tests set_identifiers fails post cache creation. * * set_identifiers cannot be called after initial cache instantiation, as you need to create a difference cache. */ public function test_set_identifiers() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/identifier', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'identifier', 'simplekeys' => true, 'simpledata' => true, 'staticacceleration' => true )); $cache = cache::make('phpunit', 'identifier', array('area')); $this->assertTrue($cache->set('contest', 'test data 1')); $this->assertEquals('test data 1', $cache->get('contest')); $this->expectException('coding_exception'); $cache->set_identifiers(array()); } /** * Tests the default application cache */ public function test_default_application_cache() { $cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'applicationtest'); $this->assertInstanceOf(cache_application::class, $cache); $this->run_on_cache($cache); $instance = cache_config_testing::instance(true); $instance->phpunit_add_definition('phpunit/test_default_application_cache', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'test_default_application_cache', 'staticacceleration' => true, 'staticaccelerationsize' => 1 )); $cache = cache::make('phpunit', 'test_default_application_cache'); $this->assertInstanceOf(cache_application::class, $cache); $this->run_on_cache($cache); } /** * Tests the default session cache */ public function test_default_session_cache() { $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'applicationtest'); $this->assertInstanceOf(cache_session::class, $cache); $this->run_on_cache($cache); } /** * Tests the default request cache */ public function test_default_request_cache() { $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'phpunit', 'applicationtest'); $this->assertInstanceOf(cache_request::class, $cache); $this->run_on_cache($cache); } /** * Tests using a cache system when there are no stores available (who knows what the admin did to achieve this). */ public function test_on_cache_without_store() { $instance = cache_config_testing::instance(true); $instance->phpunit_add_definition('phpunit/nostoretest1', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'nostoretest1', )); $instance->phpunit_add_definition('phpunit/nostoretest2', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'nostoretest2', 'staticacceleration' => true )); $instance->phpunit_remove_stores(); $cache = cache::make('phpunit', 'nostoretest1'); $this->run_on_cache($cache); $cache = cache::make('phpunit', 'nostoretest2'); $this->run_on_cache($cache); } /** * Runs a standard series of access and use tests on a cache instance. * * This function is great because we can use it to ensure all of the loaders perform exactly the same way. * * @param cache_loader $cache */ protected function run_on_cache(cache_loader $cache) { $key = 'contestkey'; $datascalars = array('test data', null); $dataarray = array('contest' => 'data', 'part' => 'two'); $dataobject = (object)$dataarray; foreach ($datascalars as $datascalar) { $this->assertTrue($cache->purge()); // Check all read methods. $this->assertFalse($cache->get($key)); $this->assertFalse($cache->has($key)); $result = $cache->get_many(array($key)); $this->assertCount(1, $result); $this->assertFalse(reset($result)); $this->assertFalse($cache->has_any(array($key))); $this->assertFalse($cache->has_all(array($key))); // Set the data. $this->assertTrue($cache->set($key, $datascalar)); // Setting it more than once should be permitted. $this->assertTrue($cache->set($key, $datascalar)); // Recheck the read methods. $this->assertEquals($datascalar, $cache->get($key)); $this->assertTrue($cache->has($key)); $result = $cache->get_many(array($key)); $this->assertCount(1, $result); $this->assertEquals($datascalar, reset($result)); $this->assertTrue($cache->has_any(array($key))); $this->assertTrue($cache->has_all(array($key))); // Delete it. $this->assertTrue($cache->delete($key)); // Check its gone. $this->assertFalse($cache->get($key)); $this->assertFalse($cache->has($key)); } // Test arrays. $this->assertTrue($cache->set($key, $dataarray)); $this->assertEquals($dataarray, $cache->get($key)); // Test objects. $this->assertTrue($cache->set($key, $dataobject)); $this->assertEquals($dataobject, $cache->get($key)); $starttime = microtime(true); $specobject = new cache_phpunit_dummy_object('red', 'blue', $starttime); $this->assertTrue($cache->set($key, $specobject)); $result = $cache->get($key); $this->assertInstanceOf(cache_phpunit_dummy_object::class, $result); $this->assertEquals('red_ptc_wfc', $result->property1); $this->assertEquals('blue_ptc_wfc', $result->property2); $this->assertGreaterThan($starttime, $result->propertytime); // Test array of objects. $specobject = new cache_phpunit_dummy_object('red', 'blue', $starttime); $data = new cacheable_object_array(array( clone($specobject), clone($specobject), clone($specobject)) ); $this->assertTrue($cache->set($key, $data)); $result = $cache->get($key); $this->assertInstanceOf(cacheable_object_array::class, $result); $this->assertCount(3, $data); foreach ($result as $item) { $this->assertInstanceOf(cache_phpunit_dummy_object::class, $item); $this->assertEquals('red_ptc_wfc', $item->property1); $this->assertEquals('blue_ptc_wfc', $item->property2); // Ensure that wake from cache is called in all cases. $this->assertGreaterThan($starttime, $item->propertytime); } // Test set many. $cache->set_many(array('key1' => 'data1', 'key2' => 'data2', 'key3' => null)); $this->assertEquals('data1', $cache->get('key1')); $this->assertEquals('data2', $cache->get('key2')); $this->assertEquals(null, $cache->get('key3')); $this->assertTrue($cache->delete('key1')); $this->assertTrue($cache->delete('key2')); $this->assertTrue($cache->delete('key3')); $cache->set_many(array( 'key1' => array(1, 2, 3), 'key2' => array(3, 2, 1), )); $this->assertIsArray($cache->get('key1')); $this->assertIsArray($cache->get('key2')); $this->assertCount(3, $cache->get('key1')); $this->assertCount(3, $cache->get('key2')); $this->assertIsArray($cache->get_many(array('key1', 'key2'))); $this->assertCount(2, $cache->get_many(array('key1', 'key2'))); $this->assertEquals(2, $cache->delete_many(array('key1', 'key2'))); // Test delete many. $this->assertTrue($cache->set('key1', 'data1')); $this->assertTrue($cache->set('key2', 'data2')); $this->assertTrue($cache->set('key3', null)); $this->assertEquals('data1', $cache->get('key1')); $this->assertEquals('data2', $cache->get('key2')); $this->assertEquals(null, $cache->get('key3')); $this->assertEquals(3, $cache->delete_many(array('key1', 'key2', 'key3'))); $this->assertFalse($cache->get('key1')); $this->assertFalse($cache->get('key2')); $this->assertFalse($cache->get('key3')); // Quick reference test. $obj = new \stdClass; $obj->key = 'value'; $ref =& $obj; $this->assertTrue($cache->set('obj', $obj)); $obj->key = 'eulav'; $var = $cache->get('obj'); $this->assertInstanceOf(\stdClass::class, $var); $this->assertEquals('value', $var->key); $ref->key = 'eulav'; $var = $cache->get('obj'); $this->assertInstanceOf(\stdClass::class, $var); $this->assertEquals('value', $var->key); $this->assertTrue($cache->delete('obj')); // Deep reference test. $obj1 = new \stdClass; $obj1->key = 'value'; $obj2 = new \stdClass; $obj2->key = 'test'; $obj3 = new \stdClass; $obj3->key = 'pork'; $obj1->subobj =& $obj2; $obj2->subobj =& $obj3; $this->assertTrue($cache->set('obj', $obj1)); $obj1->key = 'eulav'; $obj2->key = 'tset'; $obj3->key = 'krop'; $var = $cache->get('obj'); $this->assertInstanceOf(\stdClass::class, $var); $this->assertEquals('value', $var->key); $this->assertInstanceOf(\stdClass::class, $var->subobj); $this->assertEquals('test', $var->subobj->key); $this->assertInstanceOf(\stdClass::class, $var->subobj->subobj); $this->assertEquals('pork', $var->subobj->subobj->key); $this->assertTrue($cache->delete('obj')); // Death reference test... basically we don't want this to die. $obj = new \stdClass; $obj->key = 'value'; $obj->self =& $obj; $this->assertTrue($cache->set('obj', $obj)); $var = $cache->get('obj'); $this->assertInstanceOf(\stdClass::class, $var); $this->assertEquals('value', $var->key); // Reference test after retrieve. $obj = new \stdClass; $obj->key = 'value'; $this->assertTrue($cache->set('obj', $obj)); $var1 = $cache->get('obj'); $this->assertInstanceOf(\stdClass::class, $var1); $this->assertEquals('value', $var1->key); $var1->key = 'eulav'; $this->assertEquals('eulav', $var1->key); $var2 = $cache->get('obj'); $this->assertInstanceOf(\stdClass::class, $var2); $this->assertEquals('value', $var2->key); $this->assertTrue($cache->delete('obj')); // Death reference test on get_many... basically we don't want this to die. $obj = new \stdClass; $obj->key = 'value'; $obj->self =& $obj; $this->assertEquals(1, $cache->set_many(array('obj' => $obj))); $var = $cache->get_many(array('obj')); $this->assertInstanceOf(\stdClass::class, $var['obj']); $this->assertEquals('value', $var['obj']->key); // Reference test after retrieve. $obj = new \stdClass; $obj->key = 'value'; $this->assertEquals(1, $cache->set_many(array('obj' => $obj))); $var1 = $cache->get_many(array('obj')); $this->assertInstanceOf(\stdClass::class, $var1['obj']); $this->assertEquals('value', $var1['obj']->key); $var1['obj']->key = 'eulav'; $this->assertEquals('eulav', $var1['obj']->key); $var2 = $cache->get_many(array('obj')); $this->assertInstanceOf(\stdClass::class, $var2['obj']); $this->assertEquals('value', $var2['obj']->key); $this->assertTrue($cache->delete('obj')); // Test strictness exceptions. try { $cache->get('exception', MUST_EXIST); $this->fail('Exception expected from cache::get using MUST_EXIST'); } catch (\Exception $e) { $this->assertTrue(true); } try { $cache->get_many(array('exception1', 'exception2'), MUST_EXIST); $this->fail('Exception expected from cache::get_many using MUST_EXIST'); } catch (\Exception $e) { $this->assertTrue(true); } $cache->set('test', 'test'); try { $cache->get_many(array('test', 'exception'), MUST_EXIST); $this->fail('Exception expected from cache::get_many using MUST_EXIST'); } catch (\Exception $e) { $this->assertTrue(true); } } /** * Tests a definition using a data loader */ public function test_definition_data_loader() { $instance = cache_config_testing::instance(true); $instance->phpunit_add_definition('phpunit/datasourcetest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'datasourcetest', 'datasource' => 'cache_phpunit_dummy_datasource', 'datasourcefile' => 'cache/tests/fixtures/lib.php' )); $cache = cache::make('phpunit', 'datasourcetest'); $this->assertInstanceOf(cache_application::class, $cache); // Purge it to be sure. $this->assertTrue($cache->purge()); // It won't be there yet. $this->assertFalse($cache->has('Test')); // It should load it ;). $this->assertTrue($cache->has('Test', true)); // Purge it to be sure. $this->assertTrue($cache->purge()); $this->assertEquals('Test has no value really.', $cache->get('Test')); // Test multiple values. $this->assertTrue($cache->purge()); $this->assertTrue($cache->set('b', 'B')); $result = $cache->get_many(array('a', 'b', 'c')); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertArrayHasKey('a', $result); $this->assertArrayHasKey('b', $result); $this->assertArrayHasKey('c', $result); $this->assertEquals('a has no value really.', $result['a']); $this->assertEquals('B', $result['b']); $this->assertEquals('c has no value really.', $result['c']); } /**> * Tests a definition using a data loader with versioned keys. * Tests a definition using an overridden loader > * */ > * @covers ::get_versioned public function test_definition_overridden_loader() { > * @covers ::set_versioned $instance = cache_config_testing::instance(true); > */ $instance->phpunit_add_definition('phpunit/overridetest', array( > public function test_definition_data_loader_versioned() { 'mode' => cache_store::MODE_APPLICATION, > // Create two definitions, one using a non-versionable data source and the other using 'component' => 'phpunit', > // a versionable one. 'area' => 'overridetest', > $instance = cache_config_testing::instance(true); 'overrideclass' => 'cache_phpunit_dummy_overrideclass', > $instance->phpunit_add_definition('phpunit/datasourcetest1', array( 'overrideclassfile' => 'cache/tests/fixtures/lib.php' > 'mode' => cache_store::MODE_APPLICATION, )); > 'component' => 'phpunit', $cache = cache::make('phpunit', 'overridetest'); > 'area' => 'datasourcetest1', $this->assertInstanceOf(cache_phpunit_dummy_overrideclass::class, $cache); > 'datasource' => 'cache_phpunit_dummy_datasource', $this->assertInstanceOf(cache_application::class, $cache); > 'datasourcefile' => 'cache/tests/fixtures/lib.php' // Purge it to be sure. > )); $this->assertTrue($cache->purge()); > $instance->phpunit_add_definition('phpunit/datasourcetest2', array( // It won't be there yet. > 'mode' => cache_store::MODE_APPLICATION, $this->assertFalse($cache->has('Test')); > 'component' => 'phpunit', // Add it. > 'area' => 'datasourcetest2', $this->assertTrue($cache->set('Test', 'Test has no value really.')); > 'datasource' => 'cache_phpunit_dummy_datasource_versionable', // Check its there. > 'datasourcefile' => 'cache/tests/fixtures/lib.php' $this->assertEquals('Test has no value really.', $cache->get('Test')); > )); } > > // The first data source works for normal 'get'. /** > $cache1 = cache::make('phpunit', 'datasourcetest1'); * Test the mappingsonly setting. > $this->assertEquals('Frog has no value really.', $cache1->get('Frog')); */ > public function test_definition_mappings_only() { > // But it doesn't work for get_versioned. /** @var cache_config_testing $instance */ > try { $instance = cache_config_testing::instance(true); > $cache1->get_versioned('zombie', 1); $instance->phpunit_add_definition('phpunit/mappingsonly', array( > $this->fail(); 'mode' => cache_store::MODE_APPLICATION, > } catch (\coding_exception $e) { 'component' => 'phpunit', > $this->assertStringContainsString('Data source is not versionable', $e->getMessage()); 'area' => 'mappingsonly', > } 'mappingsonly' => true > ), false); > // The second data source works for get_versioned. Set up the datasource first. $instance->phpunit_add_definition('phpunit/nonmappingsonly', array( > $cache2 = cache::make('phpunit', 'datasourcetest2'); 'mode' => cache_store::MODE_APPLICATION, > 'component' => 'phpunit', > $datasource = \cache_phpunit_dummy_datasource_versionable::get_last_instance(); 'area' => 'nonmappingsonly', > $datasource->has_value('frog', 3, 'Kermit'); 'mappingsonly' => false > ), false); > // Check data with no value. > $this->assertFalse($cache2->get_versioned('zombie', 1)); $cacheonly = cache::make('phpunit', 'mappingsonly'); > $this->assertInstanceOf(cache_application::class, $cacheonly); > // Check data with value in datastore of required version. $this->assertEquals('cachestore_dummy', $cacheonly->phpunit_get_store_class()); > $result = $cache2->get_versioned('frog', 3, IGNORE_MISSING, $actualversion); > $this->assertEquals('Kermit', $result); $expected = $this->get_expected_application_cache_store(); > $this->assertEquals(3, $actualversion); $cachenon = cache::make('phpunit', 'nonmappingsonly'); > $this->assertInstanceOf(cache_application::class, $cachenon); > // Check when the datastore doesn't have required version. $this->assertEquals($expected, $cachenon->phpunit_get_store_class()); > $this->assertFalse($cache2->get_versioned('frog', 4)); } > } > /** > /*** Test a very basic definition. */ public function test_definition() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/test', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'test', )); $cache = cache::make('phpunit', 'test'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); } /** * Test a definition using the simple keys. */ public function test_definition_simplekeys() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/simplekeytest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'simplekeytest', 'simplekeys' => true )); $cache = cache::make('phpunit', 'simplekeytest'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); $cache->purge(); $this->assertTrue($cache->set('1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('1')); $this->assertTrue($cache->set('2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('2')); } /** * Test a negative TTL on an application cache. */ public function test_application_ttl_negative() { $instance = cache_config_testing::instance(true); $instance->phpunit_add_definition('phpunit/ttltest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'ttltest', 'ttl' => -86400 // Set to a day in the past to be extra sure. )); $cache = cache::make('phpunit', 'ttltest'); $this->assertInstanceOf(cache_application::class, $cache); // Purge it to be sure. $this->assertTrue($cache->purge()); // It won't be there yet. $this->assertFalse($cache->has('Test')); // Set it now. $this->assertTrue($cache->set('Test', 'Test')); // Check its not there. $this->assertFalse($cache->has('Test')); // Double check by trying to get it. $this->assertFalse($cache->get('Test')); // Test with multiple keys. $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); $result = $cache->get_many(array('a', 'b', 'c')); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertArrayHasKey('a', $result); $this->assertArrayHasKey('b', $result); $this->assertArrayHasKey('c', $result); $this->assertFalse($result['a']); $this->assertFalse($result['b']); $this->assertFalse($result['c']); // Test with multiple keys including missing ones. $result = $cache->get_many(array('a', 'c', 'e')); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertArrayHasKey('a', $result); $this->assertArrayHasKey('c', $result); $this->assertArrayHasKey('e', $result); $this->assertFalse($result['a']); $this->assertFalse($result['c']); $this->assertFalse($result['e']); } /** * Test a positive TTL on an application cache. */ public function test_application_ttl_positive() { $instance = cache_config_testing::instance(true); $instance->phpunit_add_definition('phpunit/ttltest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'ttltest', 'ttl' => 86400 // Set to a day in the future to be extra sure. )); $cache = cache::make('phpunit', 'ttltest'); $this->assertInstanceOf(cache_application::class, $cache); // Purge it to be sure. $this->assertTrue($cache->purge()); // It won't be there yet. $this->assertFalse($cache->has('Test')); // Set it now. $this->assertTrue($cache->set('Test', 'Test')); // Check its there. $this->assertTrue($cache->has('Test')); // Double check by trying to get it. $this->assertEquals('Test', $cache->get('Test')); // Test with multiple keys. $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); $result = $cache->get_many(array('a', 'b', 'c')); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertArrayHasKey('a', $result); $this->assertArrayHasKey('b', $result); $this->assertArrayHasKey('c', $result); $this->assertEquals('A', $result['a']); $this->assertEquals('B', $result['b']); $this->assertEquals('C', $result['c']); // Test with multiple keys including missing ones. $result = $cache->get_many(array('a', 'c', 'e')); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertArrayHasKey('a', $result); $this->assertArrayHasKey('c', $result); $this->assertArrayHasKey('e', $result); $this->assertEquals('A', $result['a']); $this->assertEquals('C', $result['c']); $this->assertEquals(false, $result['e']); } /** * Test a negative TTL on an session cache. */ public function test_session_ttl_positive() { $instance = cache_config_testing::instance(true); $instance->phpunit_add_definition('phpunit/ttltest', array( 'mode' => cache_store::MODE_SESSION, 'component' => 'phpunit', 'area' => 'ttltest', 'ttl' => 86400 // Set to a day in the future to be extra sure. )); $cache = cache::make('phpunit', 'ttltest'); $this->assertInstanceOf(cache_session::class, $cache); // Purge it to be sure. $this->assertTrue($cache->purge()); // It won't be there yet. $this->assertFalse($cache->has('Test')); // Set it now. $this->assertTrue($cache->set('Test', 'Test')); // Check its there. $this->assertTrue($cache->has('Test')); // Double check by trying to get it. $this->assertEquals('Test', $cache->get('Test')); // Test with multiple keys. $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); $result = $cache->get_many(array('a', 'b', 'c')); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertArrayHasKey('a', $result); $this->assertArrayHasKey('b', $result); $this->assertArrayHasKey('c', $result); $this->assertEquals('A', $result['a']); $this->assertEquals('B', $result['b']); $this->assertEquals('C', $result['c']); // Test with multiple keys including missing ones. $result = $cache->get_many(array('a', 'c', 'e')); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertArrayHasKey('a', $result); $this->assertArrayHasKey('c', $result); $this->assertArrayHasKey('e', $result); $this->assertEquals('A', $result['a']); $this->assertEquals('C', $result['c']); $this->assertEquals(false, $result['e']); } /** * Tests manual locking operations on an application cache */ public function test_application_manual_locking() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/lockingtest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'lockingtest' )); $cache1 = cache::make('phpunit', 'lockingtest'); $cache2 = clone($cache1); $this->assertTrue($cache1->set('testkey', 'test data')); $this->assertTrue($cache2->set('testkey', 'test data')); $this->assertTrue($cache1->acquire_lock('testkey')); $this->assertFalse($cache2->acquire_lock('testkey')); $this->assertTrue($cache1->check_lock_state('testkey')); $this->assertFalse($cache2->check_lock_state('testkey')); $this->assertTrue($cache1->release_lock('testkey')); $this->assertFalse($cache2->release_lock('testkey')); $this->assertTrue($cache1->set('testkey', 'test data')); $this->assertTrue($cache2->set('testkey', 'test data')); } /** * Tests application cache event invalidation */ public function test_application_event_invalidation() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'invalidationevents' => array( 'crazyevent' ) )); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); // Test invalidating a single entry. cache_helper::invalidate_by_event('crazyevent', array('testkey1')); $this->assertFalse($cache->get('testkey1')); $this->assertEquals('test data 2', $cache->get('testkey2')); $this->assertTrue($cache->set('testkey1', 'test data 1')); // Test invalidating both entries. cache_helper::invalidate_by_event('crazyevent', array('testkey1', 'testkey2')); $this->assertFalse($cache->get('testkey1')); $this->assertFalse($cache->get('testkey2')); } /** * Tests session cache event invalidation */ public function test_session_event_invalidation() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/test_session_event_invalidation', array( 'mode' => cache_store::MODE_SESSION, 'component' => 'phpunit', 'area' => 'test_session_event_invalidation', 'invalidationevents' => array( 'crazyevent' ) )); $cache = cache::make('phpunit', 'test_session_event_invalidation'); $this->assertInstanceOf(cache_session::class, $cache); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); // Test invalidating a single entry. cache_helper::invalidate_by_event('crazyevent', array('testkey1')); $this->assertFalse($cache->get('testkey1')); $this->assertEquals('test data 2', $cache->get('testkey2')); $this->assertTrue($cache->set('testkey1', 'test data 1')); // Test invalidating both entries. cache_helper::invalidate_by_event('crazyevent', array('testkey1', 'testkey2')); $this->assertFalse($cache->get('testkey1')); $this->assertFalse($cache->get('testkey2')); } /** * Tests application cache definition invalidation */ public function test_application_definition_invalidation() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/definitioninvalidation', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'definitioninvalidation' )); $cache = cache::make('phpunit', 'definitioninvalidation'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), 'testkey1'); $this->assertFalse($cache->get('testkey1')); $this->assertEquals('test data 2', $cache->get('testkey2')); $this->assertTrue($cache->set('testkey1', 'test data 1')); cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1')); $this->assertFalse($cache->get('testkey1')); $this->assertEquals('test data 2', $cache->get('testkey2')); $this->assertTrue($cache->set('testkey1', 'test data 1')); cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1', 'testkey2')); $this->assertFalse($cache->get('testkey1')); $this->assertFalse($cache->get('testkey2')); } /** * Tests session cache definition invalidation */ public function test_session_definition_invalidation() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/test_session_definition_invalidation', array( 'mode' => cache_store::MODE_SESSION, 'component' => 'phpunit', 'area' => 'test_session_definition_invalidation' )); $cache = cache::make('phpunit', 'test_session_definition_invalidation'); $this->assertInstanceOf(cache_session::class, $cache); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), 'testkey1'); $this->assertFalse($cache->get('testkey1')); $this->assertEquals('test data 2', $cache->get('testkey2')); $this->assertTrue($cache->set('testkey1', 'test data 1')); cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), array('testkey1')); $this->assertFalse($cache->get('testkey1')); $this->assertEquals('test data 2', $cache->get('testkey2')); $this->assertTrue($cache->set('testkey1', 'test data 1')); cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), array('testkey1', 'testkey2')); $this->assertFalse($cache->get('testkey1')); $this->assertFalse($cache->get('testkey2')); } /** * Tests application cache event invalidation over a distributed setup. */ public function test_distributed_application_event_invalidation() { global $CFG; // This is going to be an intense wee test. // We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a // disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally // check that it is not picked up. $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array( 'crazyevent' ) )); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); cache_helper::invalidate_by_event('crazyevent', array('testkey1')); $this->assertFalse($cache->get('testkey1')); // OK data added, data invalidated, and invalidation time has been set. // Now we need to manually add back the data and adjust the invalidation time. $hash = md5(cache_store::MODE_APPLICATION.'/phpunit/eventinvalidationtest/'.$CFG->wwwroot.'phpunit'); $timefile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las-cache/lastinvalidation-$hash.cache"; // Make sure the file is correct. $this->assertTrue(file_exists($timefile)); $timecont = serialize(cache::now(true) - 60); // Back 60sec in the past to force it to re-invalidate. make_writable_directory(dirname($timefile)); file_put_contents($timefile, $timecont); $this->assertTrue(file_exists($timefile)); $datafile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes-cache/testkey1-$hash.cache"; $datacont = serialize("test data 1"); make_writable_directory(dirname($datafile)); file_put_contents($datafile, $datacont); $this->assertTrue(file_exists($datafile)); // Test 1: Rebuild without the event and test its there. cache_factory::reset(); $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, )); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertEquals('test data 1', $cache->get('testkey1')); // Test 2: Rebuild and test the invalidation of the event via the invalidation cache. cache_factory::reset(); $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventinvalidationtest', 'simplekeys' => true, 'simpledata' => true, 'invalidationevents' => array( 'crazyevent' ) )); $cache = cache::make('phpunit', 'eventinvalidationtest'); $this->assertFalse($cache->get('testkey1')); // Test 3: Verify that an existing lastinvalidation cache file is updated when needed. // Make a new cache class. This should should invalidate testkey2. $cache = cache::make('phpunit', 'eventinvalidationtest'); // Invalidation token should have been reset. $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); // Set testkey2 data. $cache->set('testkey2', 'test data 2'); // Backdate the event invalidation time by 30 seconds. $invalidationcache = cache::make('core', 'eventinvalidation'); $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 30)); // Lastinvalidation should already be cache::now(). $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); // Set it to 15 seconds ago so that we know if it changes. $pasttime = cache::now(true) - 15; $cache->set('lastinvalidation', $pasttime); // Make a new cache class. This should not invalidate anything. cache_factory::instance()->reset_cache_instances(); $cache = cache::make('phpunit', 'eventinvalidationtest'); // Lastinvalidation shouldn't change since it was already newer than invalidation event. $this->assertEquals($pasttime, $cache->get('lastinvalidation')); // Now set the event invalidation to newer than the lastinvalidation time. $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 5)); // Make a new cache class. This should should invalidate testkey2. cache_factory::instance()->reset_cache_instances(); $cache = cache::make('phpunit', 'eventinvalidationtest'); // Lastinvalidation timestamp should have updated to cache::now(). $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); // Now simulate a purge_by_event 5 seconds ago. $invalidationcache = cache::make('core', 'eventinvalidation'); $invalidationcache->set('crazyevent', array('purged' => cache::now(true) - 5)); // Set our lastinvalidation timestamp to 15 seconds ago. $cache->set('lastinvalidation', cache::now(true) - 15); // Make a new cache class. This should invalidate the cache. cache_factory::instance()->reset_cache_instances(); $cache = cache::make('phpunit', 'eventinvalidationtest'); // Lastinvalidation timestamp should have updated to cache::now(). $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); } /** * Tests application cache event purge */ public function test_application_event_purge() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/eventpurgetest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventpurgetest', 'invalidationevents' => array( 'crazyevent' ) )); $instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'eventpurgetestaccelerated', 'staticacceleration' => true, 'invalidationevents' => array( 'crazyevent' ) )); $cache = cache::make('phpunit', 'eventpurgetest'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); // Purge the event. cache_helper::purge_by_event('crazyevent'); // Check things have been removed. $this->assertFalse($cache->get('testkey1')); $this->assertFalse($cache->get('testkey2')); // Now test the static acceleration array. $cache = cache::make('phpunit', 'eventpurgetestaccelerated'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); // Purge the event. cache_helper::purge_by_event('crazyevent'); // Check things have been removed. $this->assertFalse($cache->get('testkey1')); $this->assertFalse($cache->get('testkey2')); } /** * Tests session cache event purge */ public function test_session_event_purge() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/eventpurgetest', array( 'mode' => cache_store::MODE_SESSION, 'component' => 'phpunit', 'area' => 'eventpurgetest', 'invalidationevents' => array( 'crazyevent' ) )); $instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array( 'mode' => cache_store::MODE_SESSION, 'component' => 'phpunit', 'area' => 'eventpurgetestaccelerated', 'staticacceleration' => true, 'invalidationevents' => array( 'crazyevent' ) )); $cache = cache::make('phpunit', 'eventpurgetest'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); // Purge the event. cache_helper::purge_by_event('crazyevent'); // Check things have been removed. $this->assertFalse($cache->get('testkey1')); $this->assertFalse($cache->get('testkey2')); // Now test the static acceleration array. $cache = cache::make('phpunit', 'eventpurgetestaccelerated'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); // Purge the event. cache_helper::purge_by_event('crazyevent'); // Check things have been removed. $this->assertFalse($cache->get('testkey1')); $this->assertFalse($cache->get('testkey2')); } /** * Tests application cache definition purge */ public function test_application_definition_purge() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/definitionpurgetest', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'definitionpurgetest', 'invalidationevents' => array( 'crazyevent' ) )); $cache = cache::make('phpunit', 'definitionpurgetest'); $this->assertTrue($cache->set('testkey1', 'test data 1')); $this->assertEquals('test data 1', $cache->get('testkey1')); $this->assertTrue($cache->set('testkey2', 'test data 2')); $this->assertEquals('test data 2', $cache->get('testkey2')); // Purge the event. cache_helper::purge_by_definition('phpunit', 'definitionpurgetest'); // Check things have been removed. $this->assertFalse($cache->get('testkey1')); $this->assertFalse($cache->get('testkey2')); } /** * Test the use of an alt path. * If we can generate a config instance we are done :) */ public function test_alt_cache_path() { global $CFG; if ((defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) || !empty($CFG->altcacheconfigpath)) { $this->markTestSkipped('Skipped testing alt cache path as it is already being used.'); } $this->resetAfterTest(); $CFG->altcacheconfigpath = $CFG->dataroot.'/cache/altcacheconfigpath'; $instance = cache_config_testing::instance(); $this->assertInstanceOf(cache_config::class, $instance); } /** * Test disabling the cache stores. */ public function test_disable_stores() { $instance = cache_config_testing::instance(); $instance->phpunit_add_definition('phpunit/disabletest1', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'disabletest1' )); $instance->phpunit_add_definition('phpunit/disabletest2', array( 'mode' => cache_store::MODE_SESSION, 'component' => 'phpunit', 'area' => 'disabletest2' )); $instance->phpunit_add_definition('phpunit/disabletest3', array( 'mode' => cache_store::MODE_REQUEST, 'component' => 'phpunit', 'area' => 'disabletest3' )); $caches = array( 'disabletest1' => cache::make('phpunit', 'disabletest1'), 'disabletest2' => cache::make('phpunit', 'disabletest2'), 'disabletest3' => cache::make('phpunit', 'disabletest3') ); $this->assertInstanceOf(cache_phpunit_application::class, $caches['disabletest1']); $this->assertInstanceOf(cache_phpunit_session::class, $caches['disabletest2']); $this->assertInstanceOf(cache_phpunit_request::class, $caches['disabletest3']); $this->assertEquals('cachestore_file', $caches['disabletest1']->phpunit_get_store_class()); $this->assertEquals('cachestore_session', $caches['disabletest2']->phpunit_get_store_class()); $this->assertEquals('cachestore_static', $caches['disabletest3']->phpunit_get_store_class()); foreach ($caches as $cache) { $this->assertFalse($cache->get('test')); $this->assertTrue($cache->set('test', 'test')); $this->assertEquals('test', $cache->get('test')); } cache_factory::disable_stores(); $caches = array( 'disabletest1' => cache::make('phpunit', 'disabletest1'), 'disabletest2' => cache::make('phpunit', 'disabletest2'), 'disabletest3' => cache::make('phpunit', 'disabletest3') ); $this->assertInstanceOf(cache_phpunit_application::class, $caches['disabletest1']); $this->assertInstanceOf(cache_phpunit_session::class, $caches['disabletest2']); $this->assertInstanceOf(cache_phpunit_request::class, $caches['disabletest3']); $this->assertEquals('cachestore_dummy', $caches['disabletest1']->phpunit_get_store_class()); $this->assertEquals('cachestore_dummy', $caches['disabletest2']->phpunit_get_store_class()); $this->assertEquals('cachestore_dummy', $caches['disabletest3']->phpunit_get_store_class()); foreach ($caches as $cache) { $this->assertFalse($cache->get('test')); $this->assertTrue($cache->set('test', 'test')); $this->assertEquals('test', $cache->get('test')); } } /** * Test disabling the cache. */ public function test_disable() { global $CFG; if ((defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) || !empty($CFG->altcacheconfigpath)) { // We can't run this test as it requires us to delete the cache configuration script which we just // cant do with a custom path in play. $this->markTestSkipped('Skipped testing cache disable functionality as alt cache path is being used.'); } $configfile = $CFG->dataroot.'/muc/config.php'; // The config file will not exist yet as we've not done anything with the cache. // reset_all_data removes the file and without a call to create a configuration it doesn't exist // as yet. $this->assertFileDoesNotExist($configfile); // Disable the cache cache_phpunit_factory::phpunit_disable(); // Check we get the expected disabled factory. $factory = cache_factory::instance(); $this->assertInstanceOf(cache_factory_disabled::class, $factory); // Check we get the expected disabled config. $config = $factory->create_config_instance(); $this->assertInstanceOf(cache_config_disabled::class, $config); // Check we get the expected disabled caches. $cache = cache::make('core', 'string'); $this->assertInstanceOf(cache_disabled::class, $cache); // Test an application cache. $cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'disable'); $this->assertInstanceOf(cache_disabled::class, $cache); $this->assertFalse($cache->get('test'));> $this->assertFalse($cache->get_versioned('v', 1));$this->assertFalse($cache->set('test', 'test'));> $this->assertFalse($cache->set_versioned('v', 1, 'data'));$this->assertFalse($cache->delete('test')); $this->assertTrue($cache->purge());> // Checking a lock should always report that we have one. > // Acquiring or releasing a lock should always report success. // Test a session cache. > $this->assertTrue($cache->check_lock_state('test')); $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'disable'); > $this->assertTrue($cache->acquire_lock('test')); $this->assertInstanceOf(cache_disabled::class, $cache); > $this->assertTrue($cache->acquire_lock('test')); > $this->assertTrue($cache->check_lock_state('test')); $this->assertFalse($cache->get('test')); > $this->assertTrue($cache->release_lock('test')); $this->assertFalse($cache->set('test', 'test')); > $this->assertTrue($cache->release_lock('test')); $this->assertFalse($cache->delete('test')); > $this->assertTrue($cache->check_lock_state('test'));$this->assertTrue($cache->purge());> $this->assertFalse($cache->get_versioned('v', 1));> $this->assertFalse($cache->set_versioned('v', 1, 'data'));// Finally test a request cache. $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'phpunit', 'disable'); $this->assertInstanceOf(cache_disabled::class, $cache); $this->assertFalse($cache->get('test'));> $this->assertFalse($cache->get_versioned('v', 1));$this->assertFalse($cache->set('test', 'test'));> $this->assertFalse($cache->set_versioned('v', 1, 'data'));$this->assertFalse($cache->delete('test')); $this->assertTrue($cache->purge()); cache_factory::reset(); $factory = cache_factory::instance(true); $config = $factory->create_config_instance(); $this->assertEquals('cache_config_testing', get_class($config)); } /** * Test that multiple application loaders work ok. */ public function test_multiple_application_loaders() { $instance = cache_config_testing::instance(true); $instance->phpunit_add_file_store('phpunittest1'); $instance->phpunit_add_file_store('phpunittest2'); $instance->phpunit_add_definition('phpunit/multi_loader', array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'multi_loader' )); $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3); $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2); $cache = cache::make('phpunit', 'multi_loader'); $this->assertInstanceOf(cache_application::class, $cache); $this->assertFalse($cache->get('test')); $this->assertTrue($cache->set('test', 'test')); $this->assertEquals('test', $cache->get('test')); $this->assertTrue($cache->delete('test')); $this->assertFalse($cache->get('test')); $this->assertTrue($cache->set('test', 'test')); $this->assertTrue($cache->purge()); $this->assertFalse($cache->get('test')); // Test the many commands. $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); $result = $cache->get_many(array('a', 'b', 'c')); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertArrayHasKey('a', $result); $this->assertArrayHasKey('b', $result); $this->assertArrayHasKey('c', $result); $this->assertEquals('A', $result['a']); $this->assertEquals('B', $result['b']); $this->assertEquals('C', $result['c']); $this->assertEquals($result, $cache->get_many(array('a', 'b', 'c'))); $this->assertEquals(2, $cache->delete_many(array('a', 'c'))); $result = $cache->get_many(array('a', 'b', 'c')); $this->assertIsArray($result); $this->assertCount(3, $result); $this->assertArrayHasKey('a', $result); $this->assertArrayHasKey('b', $result); $this->assertArrayHasKey('c', $result); $this->assertFalse($result['a']); $this->assertEquals('B', $result['b']); $this->assertFalse($result['c']); // Test non-recursive deletes. $this->assertTrue($cache->set('test', 'test')); $this->assertSame('test', $cache->get('test')); $this->assertTrue($cache->delete('test', false)); // We should still have it on a deeper loader. $this->assertSame('test', $cache->get('test')); // Test non-recusive with many functions. $this->assertSame(3, $cache->set_many(array( 'one' => 'one', 'two' => 'two', 'three' => 'three' ))); $this->assertSame('one', $cache->get('one')); $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); $this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false)); $this->assertSame('one', $cache->get('one')); $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); } /**> * Data provider to try using a TTL or non-TTL cache. * Test that multiple application loaders work ok. > * */ > * @return array public function test_multiple_session_loaders() { > */ /* @var cache_config_testing $instance */ > public function ttl_or_not(): array { $instance = cache_config_testing::instance(true); > return [[false], [true]]; $instance->phpunit_add_session_store('phpunittest1'); > } $instance->phpunit_add_session_store('phpunittest2'); > $instance->phpunit_add_definition('phpunit/multi_loader', array( > /** 'mode' => cache_store::MODE_SESSION, > * Data provider to try using a TTL or non-TTL cache, and static acceleration or not. 'component' => 'phpunit', > * 'area' => 'multi_loader' > * @return array )); > */ $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3); > public function ttl_and_static_acceleration_or_not(): array { $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2); > return [[false, false], [false, true], [true, false], [true, true]]; > } $cache = cache::make('phpunit', 'multi_loader'); > $this->assertInstanceOf(cache_session::class, $cache); > /** $this->assertFalse($cache->get('test')); > * Data provider to try using a TTL or non-TTL cache, and simple data on or off. $this->assertTrue($cache->set('test', 'test')); > * $this->assertEquals('test', $cache->get('test')); > * @return array $this->assertTrue($cache->delete('test')); > */ $this->assertFalse($cache->get('test')); > public function ttl_and_simple_data_or_not(): array { $this->assertTrue($cache->set('test', 'test')); > // Same values as for ttl and static acceleration (two booleans). $this->assertTrue($cache->purge()); > return $this->ttl_and_static_acceleration_or_not(); $this->assertFalse($cache->get('test')); > } > // Test the many commands. > /** $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); > * Shared code to set up a two or three-layer versioned cache for testing. $result = $cache->get_many(array('a', 'b', 'c')); > * $this->assertIsArray($result); > * @param bool $ttl If true, sets TTL in the definition $this->assertCount(3, $result); > * @param bool $threelayer If true, uses a 3-layer instead of 2-layer cache $this->assertArrayHasKey('a', $result); > * @param bool $staticacceleration If true, enables static acceleration $this->assertArrayHasKey('b', $result); > * @param bool $simpledata If true, enables simple data $this->assertArrayHasKey('c', $result); > * @return \cache_application Cache $this->assertEquals('A', $result['a']); > */ $this->assertEquals('B', $result['b']); > protected function create_versioned_cache(bool $ttl, bool $threelayer = false, $this->assertEquals('C', $result['c']); > bool $staticacceleration = false, bool $simpledata = false): \cache_application { $this->assertEquals($result, $cache->get_many(array('a', 'b', 'c'))); > $instance = cache_config_testing::instance(true); $this->assertEquals(2, $cache->delete_many(array('a', 'c'))); > $instance->phpunit_add_file_store('a', false); $result = $cache->get_many(array('a', 'b', 'c')); > $instance->phpunit_add_file_store('b', false); $this->assertIsArray($result); > if ($threelayer) { $this->assertCount(3, $result); > $instance->phpunit_add_file_store('c', false); $this->assertArrayHasKey('a', $result); > } $this->assertArrayHasKey('b', $result); > $defarray = [ $this->assertArrayHasKey('c', $result); > 'mode' => cache_store::MODE_APPLICATION, $this->assertFalse($result['a']); > 'component' => 'phpunit', $this->assertEquals('B', $result['b']); > 'area' => 'multi_loader' $this->assertFalse($result['c']); > ]; > if ($ttl) { // Test non-recursive deletes. > $defarray['ttl'] = '600'; $this->assertTrue($cache->set('test', 'test')); > } $this->assertSame('test', $cache->get('test')); > if ($staticacceleration) { $this->assertTrue($cache->delete('test', false)); > $defarray['staticacceleration'] = true; // We should still have it on a deeper loader. > $defarray['staticaccelerationsize'] = 10; $this->assertSame('test', $cache->get('test')); > } // Test non-recusive with many functions. > if ($simpledata) { $this->assertSame(3, $cache->set_many(array( > $defarray['simpledata'] = true; 'one' => 'one', > } 'two' => 'two', > $instance->phpunit_add_definition('phpunit/multi_loader', $defarray, false); 'three' => 'three' > $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'a', 1); ))); > $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'b', 2); $this->assertSame('one', $cache->get('one')); > if ($threelayer) { $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); > $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'c', 3); $this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false)); > } $this->assertSame('one', $cache->get('one')); > $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); > $multicache = cache::make('phpunit', 'multi_loader'); } > return $multicache; > } /** > * Test switching users with session caches. > /** */ > * Tests basic use of versioned cache. public function test_session_cache_switch_user() { > * $this->resetAfterTest(true); > * @dataProvider ttl_and_simple_data_or_not $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache'); > * @param bool $ttl If true, uses a TTL cache. $user1 = $this->getDataGenerator()->create_user(); > * @param bool $simpledata If true, turns on simple data flag $user2 = $this->getDataGenerator()->create_user(); > * @covers ::set_versioned > * @covers ::get_versioned // Log in as the first user. > */ $this->setUser($user1); > public function test_versioned_cache_basic(bool $ttl, bool $simpledata): void { $sesskey1 = sesskey(); > $multicache = $this->create_versioned_cache($ttl, false, false, $simpledata); > // Set a basic value in the cache. > $this->assertTrue($multicache->set_versioned('game', 1, 'Pooh-sticks')); $cache->set('var', 1); > $this->assertTrue($cache->has('var')); > $result = $multicache->get_versioned('game', 1, IGNORE_MISSING, $actualversion); $this->assertEquals(1, $cache->get('var')); > $this->assertEquals('Pooh-sticks', $result); > $this->assertEquals(1, $actualversion); // Change to the second user. > } $this->setUser($user2); > $sesskey2 = sesskey(); > /** > * Tests versioned cache with objects. // Make sure the cache doesn't give us the data for the last user. > * $this->assertNotEquals($sesskey1, $sesskey2); > * @dataProvider ttl_and_static_acceleration_or_not $this->assertFalse($cache->has('var')); > * @param bool $ttl If true, uses a TTL cache. $this->assertEquals(false, $cache->get('var')); > * @param bool $staticacceleration If true, enables static acceleration } > * @covers ::set_versioned > * @covers ::get_versioned /** > */ * Test switching users with session caches. > public function test_versioned_cache_objects(bool $ttl, bool $staticacceleration): void { */ > $multicache = $this->create_versioned_cache($ttl, false, $staticacceleration); public function test_session_cache_switch_user_application_mapping() { > $this->resetAfterTest(true); > // Set an object value. $instance = cache_config_testing::instance(true); > $data = (object)['game' => 'Pooh-sticks']; $instance->phpunit_add_file_store('testfilestore'); > $this->assertTrue($multicache->set_versioned('game', 1, $data)); $instance->phpunit_add_definition('phpunit/testappsession', array( > 'mode' => cache_store::MODE_SESSION, > // Get it. 'component' => 'phpunit', > $result = $multicache->get_versioned('game', 1); 'area' => 'testappsession' > $this->assertEquals('Pooh-sticks', $result->game); )); > $instance->phpunit_add_definition_mapping('phpunit/testappsession', 'testfilestore', 3); > // Mess about with the value in the returned object. $cache = cache::make('phpunit', 'testappsession'); > $result->game = 'Tag'; $user1 = $this->getDataGenerator()->create_user(); > $user2 = $this->getDataGenerator()->create_user(); > // Get it again and confirm the cached object has not been affected. > $result = $multicache->get_versioned('game', 1); // Log in as the first user. > $this->assertEquals('Pooh-sticks', $result->game); $this->setUser($user1); > } $sesskey1 = sesskey(); > > /** // Set a basic value in the cache. > * Tests requesting a version that doesn't exist. $cache->set('var', 1); > * $this->assertTrue($cache->has('var')); > * @dataProvider ttl_or_not $this->assertEquals(1, $cache->get('var')); > * @param bool $ttl If true, uses a TTL cache. > * @covers ::set_versioned // Change to the second user. > * @covers ::get_versioned $this->setUser($user2); > */ $sesskey2 = sesskey(); > public function test_versioned_cache_not_exist(bool $ttl): void { > $multicache = $this->create_versioned_cache($ttl); // Make sure the cache doesn't give us the data for the last user. > $this->assertNotEquals($sesskey1, $sesskey2); > $multicache->set_versioned('game', 1, 'Pooh-sticks'); $this->assertFalse($cache->has('var')); > $this->assertEquals(false, $cache->get('var')); > // Exists but with wrong version. } > $this->assertFalse($multicache->get_versioned('game', 2)); > /** > // Doesn't exist at all. * Test two session caches being used at once to confirm collisions don't occur. > $this->assertFalse($multicache->get_versioned('frog', 0)); */ > } public function test_dual_session_caches() { > $instance = cache_config_testing::instance(true); > /** $instance->phpunit_add_definition('phpunit/testsess1', array( > * Tests attempts to use get after set_version or get_version after set. 'mode' => cache_store::MODE_SESSION, > * 'component' => 'phpunit', > * @dataProvider ttl_or_not 'area' => 'testsess1' > * @param bool $ttl If true, uses a TTL cache. )); > * @covers ::set_versioned $instance->phpunit_add_definition('phpunit/testsess2', array( > * @covers ::get_versioned 'mode' => cache_store::MODE_SESSION, > */ 'component' => 'phpunit', > public function test_versioned_cache_incompatible_versioning(bool $ttl): void { 'area' => 'testsess2' > $multicache = $this->create_versioned_cache($ttl); )); > $cache1 = cache::make('phpunit', 'testsess1'); > // What if you use get on a get_version cache? $cache2 = cache::make('phpunit', 'testsess2'); > $multicache->set_versioned('game', 1, 'Pooh-sticks'); > try { $this->assertFalse($cache1->has('test')); > $multicache->get('game'); $this->assertFalse($cache2->has('test')); > $this->fail(); > } catch (\coding_exception $e) { $this->assertTrue($cache1->set('test', '1')); > $this->assertStringContainsString('Unexpectedly found versioned cache entry', $e->getMessage()); > } $this->assertTrue($cache1->has('test')); > $this->assertFalse($cache2->has('test')); > // Or get_version on a get cache? > $multicache->set('toy', 'Train set'); $this->assertTrue($cache2->set('test', '2')); > try { > $multicache->get_versioned('toy', 1); $this->assertEquals(1, $cache1->get('test')); > $this->fail(); $this->assertEquals(2, $cache2->get('test')); > } catch (\coding_exception $e) { > $this->assertStringContainsString('Unexpectedly found non-versioned cache entry', $e->getMessage()); $this->assertTrue($cache1->delete('test')); > } } > } > /** > /** * Test multiple session caches when switching user. > * Versions are only stored once, so if you set a newer version you will always get it even */ > * if you ask for the lower version number. public function test_session_cache_switch_user_multiple() { > * $this->resetAfterTest(true); > * @dataProvider ttl_or_not $cache1 = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache1'); > * @param bool $ttl If true, uses a TTL cache. $cache2 = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache2'); > * @covers ::set_versioned $user1 = $this->getDataGenerator()->create_user(); > * @covers ::get_versioned $user2 = $this->getDataGenerator()->create_user(); > */ > public function test_versioned_cache_single_copy(bool $ttl): void { // Log in as the first user. > $multicache = $this->create_versioned_cache($ttl); $this->setUser($user1); > $sesskey1 = sesskey(); > $multicache->set_versioned('game', 1, 'Pooh-sticks'); > $multicache->set_versioned('game', 2, 'Tag'); // Set a basic value in the caches. > $this->assertEquals('Tag', $multicache->get_versioned('game', 1, IGNORE_MISSING, $actualversion)); $cache1->set('var', 1); > $cache2->set('var', 2); > // The reported version number matches the one returned, not requested. $this->assertEquals(1, $cache1->get('var')); > $this->assertEquals(2, $actualversion); $this->assertEquals(2, $cache2->get('var')); > } > // Change to the second user. > /** $this->setUser($user2); > * If the first (local) store has an outdated copy but the second (shared) store has a newer $sesskey2 = sesskey(); > * one, then it should automatically be retrieved. > * // Make sure the cache doesn't give us the data for the last user. > * @dataProvider ttl_or_not // Also make sure that switching the user has lead to both caches being purged. > * @param bool $ttl If true, uses a TTL cache. $this->assertNotEquals($sesskey1, $sesskey2); > * @covers ::set_versioned $this->assertEquals(false, $cache1->get('var')); > * @covers ::get_versioned $this->assertEquals(false, $cache2->get('var')); > */ } > public function test_versioned_cache_outdated_local(bool $ttl): void { > $multicache = $this->create_versioned_cache($ttl); /** > * Test application locking. > // Set initial value to version 2, 'Tag', in both stores. */ > $multicache->set_versioned('game', 2, 'Tag'); public function test_application_locking() { > $instance = cache_config_testing::instance(true); > // Get the two separate cache stores for the multi-level cache. $instance->phpunit_add_definition('phpunit/test_application_locking', array( > $factory = cache_factory::instance(); 'mode' => cache_store::MODE_APPLICATION, > $definition = $factory->create_definition('phpunit', 'multi_loader'); 'component' => 'phpunit', > [0 => $storea, 1 => $storeb] = $factory->get_store_instances_in_use($definition); 'area' => 'test_application_locking', > 'staticacceleration' => true, > // Simulate what happens if the shared cache is updated with a new version but the 'staticaccelerationsize' => 1, > // local one still has an old version. 'requirelockingread' => true, > $hashgame = cache_helper::hash_key('game', $definition); 'requirelockingwrite' => true > $data = 'British Bulldog'; )); > if ($ttl) { $cache = cache::make('phpunit', 'test_application_locking'); > $data = new \cache_ttl_wrapper($data, 600); $this->assertInstanceOf(cache_application::class, $cache); > } > $storeb->set($hashgame, new \core_cache\version_wrapper($data, 3)); $this->assertTrue($cache->set('a', 'A')); > $this->assertTrue($cache->set('b', 'B')); > // If we ask for the old one we'll get it straight off from local cache. $this->assertTrue($cache->set('c', 'C')); > $this->assertEquals('Tag', $multicache->get_versioned('game', 2)); $this->assertEquals('A', $cache->get('a')); > $this->assertEquals(array('b' => 'B', 'c' => 'C'), $cache->get_many(array('b', 'c'))); > // But if we ask for the new one it will still get it via the shared cache. $this->assertTrue($cache->delete('a')); > $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 3)); $this->assertFalse($cache->has('a')); > } > // Also, now it will have been updated in the local cache as well. > $localvalue = $storea->get($hashgame); /** > if ($ttl) { * Test the static cache_helper method purge_stores_used_by_definition. > // In case the time has changed slightly since the first set, we can't do an exact */ > // compare, so check it ignoring the time field. public function test_purge_stores_used_by_definition() { > $this->assertEquals(3, $localvalue->version); $instance = cache_config_testing::instance(true); > $ttldata = $localvalue->data; $instance->phpunit_add_definition('phpunit/test_purge_stores_used_by_definition', array( > $this->assertInstanceOf('cache_ttl_wrapper', $ttldata); 'mode' => cache_store::MODE_APPLICATION, > $this->assertEquals('British Bulldog', $ttldata->data); 'component' => 'phpunit', > } else { 'area' => 'test_purge_stores_used_by_definition' > $this->assertEquals(new \core_cache\version_wrapper('British Bulldog', 3), $localvalue); )); > } $cache = cache::make('phpunit', 'test_purge_stores_used_by_definition'); > } $this->assertInstanceOf(cache_application::class, $cache); > $this->assertTrue($cache->set('test', 'test')); > /** unset($cache); > * When we request a newer version, older ones are automatically deleted in every level of the > * cache (to save I/O if there are multiple requests, as if there is another request it will cache_helper::purge_stores_used_by_definition('phpunit', 'test_purge_stores_used_by_definition'); > * not have to retrieve the values to find out that they're old). > * $cache = cache::make('phpunit', 'test_purge_stores_used_by_definition'); > * @dataProvider ttl_or_not $this->assertInstanceOf(cache_application::class, $cache); > * @param bool $ttl If true, uses a TTL cache. $this->assertFalse($cache->get('test')); > * @covers ::set_versioned } > * @covers ::get_versioned > */ /** > public function test_versioned_cache_deleting_outdated(bool $ttl): void { * Test purge routines. > $multicache = $this->create_versioned_cache($ttl); */ > public function test_purge_routines() { > // Set initial value to version 2, 'Tag', in both stores. $instance = cache_config_testing::instance(true); > $multicache->set_versioned('game', 2, 'Tag'); $instance->phpunit_add_definition('phpunit/purge1', array( > 'mode' => cache_store::MODE_APPLICATION, > // Get the two separate cache stores for the multi-level cache. 'component' => 'phpunit', > $factory = cache_factory::instance(); 'area' => 'purge1' > $definition = $factory->create_definition('phpunit', 'multi_loader'); )); > [0 => $storea, 1 => $storeb] = $factory->get_store_instances_in_use($definition); $instance->phpunit_add_definition('phpunit/purge2', array( > 'mode' => cache_store::MODE_APPLICATION, > // If we request a newer version, then any older version should be deleted in each 'component' => 'phpunit', > // cache level. 'area' => 'purge2', > $this->assertFalse($multicache->get_versioned('game', 4)); 'requireidentifiers' => array( > $hashgame = cache_helper::hash_key('game', $definition); 'id' > $this->assertFalse($storea->get($hashgame)); ) > $this->assertFalse($storeb->get($hashgame)); )); > } > $factory = cache_factory::instance(); > /** $definition = $factory->create_definition('phpunit', 'purge1'); > * Tests a versioned cache when using static cache. $this->assertFalse($definition->has_required_identifiers()); > * $cache = $factory->create_cache($definition); > * @covers ::set_versioned $this->assertInstanceOf(cache_application::class, $cache); > * @covers ::get_versioned $this->assertTrue($cache->set('test', 'test')); > */ $this->assertTrue($cache->has('test')); > public function test_versioned_cache_static(): void { cache_helper::purge_by_definition('phpunit', 'purge1'); > $staticcache = $this->create_versioned_cache(false, false, true); $this->assertFalse($cache->has('test')); > > // Set a value in the cache, version 1. This will store it in static acceleration. $factory = cache_factory::instance(); > $staticcache->set_versioned('game', 1, 'Pooh-sticks'); $definition = $factory->create_definition('phpunit', 'purge2'); > $this->assertTrue($definition->has_required_identifiers()); > // Get the first cache store (we don't need the second one for this test). $cache = $factory->create_cache($definition); > $factory = cache_factory::instance(); $this->assertInstanceOf(cache_application::class, $cache); > $definition = $factory->create_definition('phpunit', 'multi_loader'); $this->assertTrue($cache->set('test', 'test')); > [0 => $storea] = $factory->get_store_instances_in_use($definition); $this->assertTrue($cache->has('test')); > cache_helper::purge_stores_used_by_definition('phpunit', 'purge2'); > // Hack a newer version into cache store without directly calling set (now the static $this->assertFalse($cache->has('test')); > // has v1, store has v2). This simulates another client updating the cache. > $hashgame = cache_helper::hash_key('game', $definition); try { > $storea->set($hashgame, new \core_cache\version_wrapper('Tag', 2)); cache_helper::purge_by_definition('phpunit', 'purge2'); > $this->fail('Should not be able to purge a definition required identifiers without providing them.'); > // Get the key from the cache, v1. This will use static acceleration. } catch (\coding_exception $ex) { > $this->assertEquals('Pooh-sticks', $staticcache->get_versioned('game', 1)); $this->assertStringContainsString('Identifier required for cache has not been provided', $ex->getMessage()); > } > // Now if we ask for a newer version, it should not use the static cached one. } > $this->assertEquals('Tag', $staticcache->get_versioned('game', 2)); > /** > // This get should have updated static acceleration, so it will be used next time without * Tests that ad-hoc caches are correctly purged with a purge_all call. > // a store request. */ > $storea->set($hashgame, new \core_cache\version_wrapper('British Bulldog', 3)); public function test_purge_all_with_adhoc_caches() { > $this->assertEquals('Tag', $staticcache->get_versioned('game', 2)); $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'core_cache', 'test'); > $cache->set('test', 123); > // Requesting the higher version will get rid of static acceleration again. cache_helper::purge_all(); > $this->assertEquals('British Bulldog', $staticcache->get_versioned('game', 3)); $this->assertFalse($cache->get('test')); > } > // Finally ask for a version that doesn't exist anywhere, just to confirm it returns null. > $this->assertFalse($staticcache->get_versioned('game', 4)); /** > } * Test that the default stores all support searching. > */ > /** public function test_defaults_support_searching() { > * Tests basic use of 3-layer versioned caches. $instance = cache_config_testing::instance(true); > * $instance->phpunit_add_definition('phpunit/search1', array( > * @covers ::set_versioned 'mode' => cache_store::MODE_APPLICATION, > * @covers ::get_versioned 'component' => 'phpunit', > */ 'area' => 'search1', > public function test_versioned_cache_3_layers_basic(): void { 'requiresearchable' => true > $multicache = $this->create_versioned_cache(false, true); )); > $instance->phpunit_add_definition('phpunit/search2', array( > // Basic use of set_versioned and get_versioned. 'mode' => cache_store::MODE_SESSION, > $multicache->set_versioned('game', 1, 'Pooh-sticks'); 'component' => 'phpunit', > $this->assertEquals('Pooh-sticks', $multicache->get_versioned('game', 1)); 'area' => 'search2', > 'requiresearchable' => true > // What if you ask for a version that doesn't exist? )); > $this->assertFalse($multicache->get_versioned('game', 2)); $instance->phpunit_add_definition('phpunit/search3', array( > 'mode' => cache_store::MODE_REQUEST, > // Setting a new version wipes out the old version; if you request it, you get the new one. 'component' => 'phpunit', > $multicache->set_versioned('game', 2, 'Tag'); 'area' => 'search3', > $this->assertEquals('Tag', $multicache->get_versioned('game', 1)); 'requiresearchable' => true > } )); > $factory = cache_factory::instance(); > /** > * Tests use of 3-layer versioned caches where the 3 layers currently have different versions. // Test application cache is searchable. > * $definition = $factory->create_definition('phpunit', 'search1'); > * @covers ::set_versioned $this->assertInstanceOf(cache_definition::class, $definition); > * @covers ::get_versioned $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); > */ $cache = $factory->create_cache($definition); > public function test_versioned_cache_3_layers_different_data(): void { $this->assertInstanceOf(cache_application::class, $cache); > // Set version 2 using normal method. $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); > $multicache = $this->create_versioned_cache(false, true); > $multicache->set_versioned('game', 2, 'Tag'); // Test session cache is searchable. > $definition = $factory->create_definition('phpunit', 'search2'); > // Get the three separate cache stores for the multi-level cache. $this->assertInstanceOf(cache_definition::class, $definition); > $factory = cache_factory::instance(); $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); > $definition = $factory->create_definition('phpunit', 'multi_loader'); $cache = $factory->create_cache($definition); > [0 => $storea, 1 => $storeb, 2 => $storec] = $factory->get_store_instances_in_use($definition); $this->assertInstanceOf(cache_session::class, $cache); > $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); > // Set up two other versions so every level has a different version. > $hashgame = cache_helper::hash_key('game', $definition); // Test request cache is searchable. > $storeb->set($hashgame, new \core_cache\version_wrapper('British Bulldog', 3)); $definition = $factory->create_definition('phpunit', 'search3'); > $storec->set($hashgame, new \core_cache\version_wrapper('Hopscotch', 4)); $this->assertInstanceOf(cache_definition::class, $definition); > $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); > // First request can be satisfied from A; second request requires B... $cache = $factory->create_cache($definition); > $this->assertEquals('Tag', $multicache->get_versioned('game', 2)); $this->assertInstanceOf(cache_request::class, $cache); > $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 3)); $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); > } > // And should update the data in A. > $this->assertEquals(new \core_cache\version_wrapper('British Bulldog', 3), $storea->get($hashgame)); /** > $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 1)); * Test static acceleration > * > // But newer data should still be in C. * Note: All the assertGreaterThanOrEqual() in this test should be assertGreaterThan() be because of some microtime() > $this->assertEquals('Hopscotch', $multicache->get_versioned('game', 4)); * resolution problems under some OSs / PHP versions, we are accepting equal as valid outcome. For more info see MDL-57147. > // Now it's stored in A and B too. */ > $this->assertEquals(new \core_cache\version_wrapper('Hopscotch', 4), $storea->get($hashgame)); public function test_static_acceleration() { > $this->assertEquals(new \core_cache\version_wrapper('Hopscotch', 4), $storeb->get($hashgame)); $instance = cache_config_testing::instance(); > } $instance->phpunit_add_definition('phpunit/accelerated', array( > 'mode' => cache_store::MODE_APPLICATION, > /**'component' => 'phpunit',> * The application locking feature should work with caches that support multiple identifiers 'area' => 'accelerated', > * (static cache and MongoDB with a specific setting). 'staticacceleration' => true, > * 'staticaccelerationsize' => 3, > * @covers \cache_application )); > */ $instance->phpunit_add_definition('phpunit/accelerated2', array( > public function test_application_locking_multiple_identifier_cache() { 'mode' => cache_store::MODE_APPLICATION, > // Get an arbitrary definition (modinfo). 'component' => 'phpunit', > $instance = cache_config_testing::instance(true); 'area' => 'accelerated2', > $definitions = $instance->get_definitions(); 'staticacceleration' => true, > $definition = \cache_definition::load('phpunit', $definitions['core/coursemodinfo']); 'staticaccelerationsize' => 3, > )); > // Set up a static cache using that definition, wrapped in cache_application so we can do $instance->phpunit_add_definition('phpunit/accelerated3', array( > // locking. 'mode' => cache_store::MODE_APPLICATION, > $store = new \cachestore_static('test'); 'component' => 'phpunit', > $store->initialise($definition); 'area' => 'accelerated3', > $cache = new cache_application($definition, $store); 'staticacceleration' => true, > 'staticaccelerationsize' => 3, > // Test the three locking functions. )); > $cache->acquire_lock('frog'); $instance->phpunit_add_definition('phpunit/accelerated4', array( > $this->assertTrue($cache->check_lock_state('frog')); 'mode' => cache_store::MODE_APPLICATION, > $cache->release_lock('frog'); 'component' => 'phpunit', > } 'area' => 'accelerated4', > 'staticacceleration' => true, > /** 'staticaccelerationsize' => 4, > * Test requiring a lock before attempting to set a key. )); > * $instance->phpunit_add_definition('phpunit/simpledataarea1', array( > * @covers ::set_implementation 'mode' => cache_store::MODE_APPLICATION, > */ 'component' => 'phpunit', > public function test_application_locking_before_write() { 'area' => 'simpledataarea1', > $instance = cache_config_testing::instance(true); 'staticacceleration' => true, > $instance->phpunit_add_definition('phpunit/test_application_locking', array( 'simpledata' => false > 'mode' => cache_store::MODE_APPLICATION, )); > 'component' => 'phpunit', $instance->phpunit_add_definition('phpunit/simpledataarea2', array( > 'area' => 'test_application_locking', 'mode' => cache_store::MODE_APPLICATION, > 'staticacceleration' => true, 'component' => 'phpunit', > 'staticaccelerationsize' => 1, 'area' => 'simpledataarea2', > 'requirelockingbeforewrite' => true 'staticacceleration' => true, > )); 'simpledata' => true > $cache = cache::make('phpunit', 'test_application_locking'); )); > $this->assertInstanceOf(cache_application::class, $cache); > $cache = cache::make('phpunit', 'accelerated'); > $cache->acquire_lock('a'); $this->assertInstanceOf(cache_phpunit_application::class, $cache); > $this->assertTrue($cache->set('a', 'A')); > $cache->release_lock('a'); // Set and get three elements. > $this->assertTrue($cache->set('a', 'A')); > $this->expectExceptionMessage('Attempted to set cache key "b" without a lock. ' $this->assertTrue($cache->set('b', 'B')); > . 'Locking before writes is required for phpunit/test_application_locking'); $this->assertTrue($cache->set('c', 'C')); > $this->assertFalse($cache->set('b', 'B')); $this->assertEquals('A', $cache->get('a')); > } $this->assertEquals(array('b' => 'B', 'c' => 'C'), $cache->get_many(array('b', 'c'))); > > // Make sure all items are in static acceleration array. > /** $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); > * Test that invalid lock setting combinations are caught. $this->assertEquals('B', $cache->phpunit_static_acceleration_get('b')); > * $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); > * @covers ::make > */ // Add new value and make sure it is in cache and it is in array. > public function test_application_conflicting_locks() { $this->assertTrue($cache->set('d', 'D')); > $instance = cache_config_testing::instance(true); $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); > $instance->phpunit_add_definition('phpunit/test_application_locking', array( $this->assertEquals('D', $cache->get('d')); > 'mode' => cache_store::MODE_APPLICATION, > 'component' => 'phpunit', // Now the least recent accessed item (a) is no longer in acceleration array. > 'area' => 'test_application_locking', $this->assertFalse($cache->phpunit_static_acceleration_get('a')); > 'staticacceleration' => true, $this->assertEquals('B', $cache->phpunit_static_acceleration_get('b')); > 'staticaccelerationsize' => 1, $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); > 'requirelockingwrite' => true, > 'requirelockingbeforewrite' => true, // Adding and deleting element. > )); $this->assertTrue($cache->set('a', 'A')); > $this->assertTrue($cache->delete('a')); > $this->expectException('coding_exception'); $this->assertFalse($cache->phpunit_static_acceleration_get('a')); > cache::make('phpunit', 'test_application_locking'); $this->assertFalse($cache->has('a')); > } > // Make sure "purge" deletes from the array as well. > /** $cache->purge(); > * Test that locking before write works when writing across multiple layers. $this->assertFalse($cache->phpunit_static_acceleration_get('a')); > * $this->assertFalse($cache->phpunit_static_acceleration_get('b')); > * @covers \cache_loader $this->assertFalse($cache->phpunit_static_acceleration_get('c')); > * @return void $this->assertFalse($cache->phpunit_static_acceleration_get('d')); > */ $this->assertFalse($cache->phpunit_static_acceleration_get('e')); > public function test_application_locking_multiple_layers() { > // Check that the array holds the last accessed items by get/set. > $instance = cache_config_testing::instance(true); $this->assertTrue($cache->set('a', 'A')); > $instance->phpunit_add_definition('phpunit/test_application_locking', array( $this->assertTrue($cache->set('b', 'B')); > 'mode' => cache_store::MODE_APPLICATION, $this->assertTrue($cache->set('c', 'C')); > 'component' => 'phpunit', $this->assertTrue($cache->set('d', 'D')); > 'area' => 'test_application_locking', $this->assertTrue($cache->set('e', 'E')); > 'staticacceleration' => true, $this->assertFalse($cache->phpunit_static_acceleration_get('a')); > 'staticaccelerationsize' => 1, $this->assertFalse($cache->phpunit_static_acceleration_get('b')); > 'requirelockingbeforewrite' => true $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); > ), false); $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); > $instance->phpunit_add_file_store('phpunittest1'); $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); > $instance->phpunit_add_file_store('phpunittest2'); > $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest1', 1); // Store a cacheable_object, get many times and ensure each time wake_for_cache is used. > $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest2', 2); // Both get and get_many are tested. Two cache entries are used to ensure the times aren't > // confused with multiple calls to get()/get_many(). > $cache = cache::make('phpunit', 'test_application_locking'); $startmicrotime = microtime(true); > $this->assertInstanceOf(cache_application::class, $cache); $cacheableobject = new cache_phpunit_dummy_object(1, 1, $startmicrotime); > $cacheableobject2 = new cache_phpunit_dummy_object(2, 2, $startmicrotime); > // Check that we can set a key across multiple layers. $this->assertTrue($cache->set('a', $cacheableobject)); > $cache->acquire_lock('a'); $this->assertTrue($cache->set('b', $cacheableobject2)); > $this->assertTrue($cache->set('a', 'A')); $staticaccelerationreturntime = $cache->phpunit_static_acceleration_get('a')->propertytime; > $cache->release_lock('a'); $staticaccelerationreturntimeb = $cache->phpunit_static_acceleration_get('b')->propertytime; > $this->assertGreaterThanOrEqual($startmicrotime, $staticaccelerationreturntime, 'Restore time of static must be newer.'); > // Delete from the current layer. > $cache->delete('a', false); // Reset the static cache without resetting backing store. > $cache->phpunit_static_acceleration_purge(); > // Check that we can get the value from the deeper layer, which will also re-set it in the current one. > $this->assertEquals('A', $cache->get('a')); // Get the value from the backend store, populating the static cache. > $cachevalue = $cache->get('a'); > // Try set/delete/get_many. $this->assertInstanceOf(cache_phpunit_dummy_object::class, $cachevalue); > $cache->acquire_lock('x'); $this->assertGreaterThanOrEqual($staticaccelerationreturntime, $cachevalue->propertytime); > $cache->acquire_lock('y'); $backingstorereturntime = $cachevalue->propertytime; > $cache->acquire_lock('z'); > $this->assertEquals(3, $cache->set_many(['x' => 'X', 'y' => 'Y', 'z' => 'Z'])); $results = $cache->get_many(array('b')); > $cache->release_lock('x'); $this->assertInstanceOf(cache_phpunit_dummy_object::class, $results['b']); > $cache->release_lock('y'); $this->assertGreaterThanOrEqual($staticaccelerationreturntimeb, $results['b']->propertytime); > $cache->release_lock('z'); $backingstorereturntimeb = $results['b']->propertytime; > > $cache->delete_many(['x', 'y', 'z'], false); // Obtain the value again and confirm that static cache is using wake_from_cache. > $this->assertEquals(['x' => 'X', 'y' => 'Y', 'z' => 'Z'], $cache->get_many(['x', 'y', 'z'])); // Upon failure, the times are not adjusted as wake_from_cache is skipped as the > // value is stored serialized in the static acceleration cache. > $cache->purge(); $cachevalue = $cache->phpunit_static_acceleration_get('a'); > $this->assertInstanceOf(cache_phpunit_dummy_object::class, $cachevalue); > // Try the tests again with a third layer. $this->assertGreaterThanOrEqual($backingstorereturntime, $cachevalue->propertytime); > $instance->phpunit_add_file_store('phpunittest3'); > $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest3', 3); $results = $cache->get_many(array('b')); > $cache = cache::make('phpunit', 'test_application_locking'); $this->assertInstanceOf(cache_phpunit_dummy_object::class, $results['b']); > $this->assertInstanceOf(cache_application::class, $cache); $this->assertGreaterThanOrEqual($backingstorereturntimeb, $results['b']->propertytime); > > // Check that we can set a key across multiple layers. /** @var cache_phpunit_application $cache */ > $cache->acquire_lock('a'); $cache = cache::make('phpunit', 'accelerated2'); > $this->assertTrue($cache->set('a', 'A')); $this->assertInstanceOf(cache_phpunit_application::class, $cache); > $cache->release_lock('a'); > // Check that the array holds the last accessed items by get/set. > // Delete from the current layer. $this->assertTrue($cache->set('a', 'A')); > $cache->delete('a', false); $this->assertTrue($cache->set('b', 'B')); > $this->assertTrue($cache->set('c', 'C')); > // Check that we can get the value from the deeper layer, which will also re-set it in the current one. $this->assertTrue($cache->set('d', 'D')); > $this->assertEquals('A', $cache->get('a')); $this->assertTrue($cache->set('e', 'E')); > // Current keys in the array: c, d, e. > // Try set/delete/get_many. $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); > $cache->acquire_lock('x'); $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); > $cache->acquire_lock('y'); $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); > $cache->acquire_lock('z'); $this->assertFalse($cache->phpunit_static_acceleration_get('a')); > $this->assertEquals(3, $cache->set_many(['x' => 'X', 'y' => 'Y', 'z' => 'Z'])); $this->assertFalse($cache->phpunit_static_acceleration_get('b')); > $cache->release_lock('x'); > $cache->release_lock('y'); $this->assertEquals('A', $cache->get('a')); > $cache->release_lock('z'); // Current keys in the array: d, e, a. > $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); > $cache->delete_many(['x', 'y', 'z'], false); $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); > $this->assertEquals(['x' => 'X', 'y' => 'Y', 'z' => 'Z'], $cache->get_many(['x', 'y', 'z'])); $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); > } $this->assertFalse($cache->phpunit_static_acceleration_get('b')); > $this->assertFalse($cache->phpunit_static_acceleration_get('c')); > /** > * Tests that locking fails correctly when either layer of a 2-layer cache has a lock already. // Current keys in the array: d, e, a. > * $this->assertEquals(array('c' => 'C'), $cache->get_many(array('c'))); > * @covers \cache_loader // Current keys in the array: e, a, c. > */ $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); > public function test_application_locking_multiple_layers_failures(): void { $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); > $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); > $instance = cache_config_testing::instance(true); $this->assertFalse($cache->phpunit_static_acceleration_get('b')); > $instance->phpunit_add_definition('phpunit/test_application_locking', array( $this->assertFalse($cache->phpunit_static_acceleration_get('d')); > 'mode' => cache_store::MODE_APPLICATION, > 'component' => 'phpunit', > 'area' => 'test_application_locking', $cache = cache::make('phpunit', 'accelerated3'); > 'staticacceleration' => true, $this->assertInstanceOf(cache_phpunit_application::class, $cache); > 'staticaccelerationsize' => 1, > 'requirelockingbeforewrite' => true // Check that the array holds the last accessed items by get/set. > ), false); $this->assertTrue($cache->set('a', 'A')); > $instance->phpunit_add_file_store('phpunittest1'); $this->assertTrue($cache->set('b', 'B')); > $instance->phpunit_add_file_store('phpunittest2'); $this->assertTrue($cache->set('c', 'C')); > $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest1', 1); $this->assertTrue($cache->set('d', 'D')); > $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest2', 2); $this->assertTrue($cache->set('e', 'E')); > $this->assertFalse($cache->phpunit_static_acceleration_get('a')); > $cache = cache::make('phpunit', 'test_application_locking'); $this->assertFalse($cache->phpunit_static_acceleration_get('b')); > $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); > // We need to get the individual stores so as to set up the right behaviour here. $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); > $ref = new \ReflectionClass('\cache'); $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); > $definitionprop = $ref->getProperty('definition'); > $definitionprop->setAccessible(true); $this->assertTrue($cache->set('b', 'B2')); > $storeprop = $ref->getProperty('store'); $this->assertFalse($cache->phpunit_static_acceleration_get('a')); > $storeprop->setAccessible(true); $this->assertEquals('B2', $cache->phpunit_static_acceleration_get('b')); > $loaderprop = $ref->getProperty('loader'); $this->assertFalse($cache->phpunit_static_acceleration_get('c')); > $loaderprop->setAccessible(true); $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); > $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); > $definition = $definitionprop->getValue($cache); > $localstore = $storeprop->getValue($cache); $this->assertEquals(2, $cache->set_many(array('b' => 'B3', 'c' => 'C3'))); > $sharedcache = $loaderprop->getValue($cache); $this->assertFalse($cache->phpunit_static_acceleration_get('a')); > $sharedstore = $storeprop->getValue($sharedcache); $this->assertEquals('B3', $cache->phpunit_static_acceleration_get('b')); > $this->assertEquals('C3', $cache->phpunit_static_acceleration_get('c')); > // Set the lock waiting time to 1 second so it doesn't take forever to run the test. $this->assertFalse($cache->phpunit_static_acceleration_get('d')); > $ref = new \ReflectionClass('\cachestore_file'); $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); > $lockwaitprop = $ref->getProperty('lockwait'); > $lockwaitprop->setAccessible(true); $cache = cache::make('phpunit', 'accelerated4'); > $this->assertInstanceOf(cache_phpunit_application::class, $cache); > $lockwaitprop->setValue($localstore, 1); $this->assertTrue($cache->set('a', 'A')); > $lockwaitprop->setValue($sharedstore, 1); $this->assertTrue($cache->set('a', 'A')); > $this->assertTrue($cache->set('a', 'A')); > // Get key details and the cache identifier. $this->assertTrue($cache->set('a', 'A')); > $hashedkey = cache_helper::hash_key('apple', $definition); $this->assertTrue($cache->set('a', 'A')); > $localidentifier = $cache->get_identifier(); $this->assertTrue($cache->set('a', 'A')); > $sharedidentifier = $sharedcache->get_identifier(); $this->assertTrue($cache->set('a', 'A')); > $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); > // 1. Local cache is not locked but parent cache is locked. $this->assertEquals('A', $cache->get('a')); > $sharedstore->acquire_lock($hashedkey, 'somebodyelse'); > try { // Setting simpledata to false objects are cloned when retrieving data. > $this->assertFalse($cache->acquire_lock('apple')); $cache = cache::make('phpunit', 'simpledataarea1'); > $notreallysimple = new \stdClass(); > // Neither store is locked by us, shared store still locked. $notreallysimple->name = 'a'; > $this->assertFalse((bool)$localstore->check_lock_state($hashedkey, $localidentifier)); $cache->set('a', $notreallysimple); > $this->assertFalse((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier)); $returnedinstance1 = $cache->get('a'); > $this->assertTrue((bool)$sharedstore->check_lock_state($hashedkey, 'somebodyelse')); $returnedinstance2 = $cache->get('a'); > $returnedinstance1->name = 'b'; > } finally { $this->assertEquals('a', $returnedinstance2->name); > $sharedstore->release_lock($hashedkey, 'somebodyelse'); > } // Setting simpledata to true we assume that data does not contain references. > $cache = cache::make('phpunit', 'simpledataarea2'); > // 2. Local cache is locked, parent cache is not locked. $notreallysimple = new \stdClass(); > $localstore->acquire_lock($hashedkey, 'somebodyelse'); $notreallysimple->name = 'a'; > try { $cache->set('a', $notreallysimple); > $this->assertFalse($cache->acquire_lock('apple')); $returnedinstance1 = $cache->get('a'); > $returnedinstance2 = $cache->get('a'); > // Neither store is locked by us, local store still locked. $returnedinstance1->name = 'b'; > $this->assertFalse((bool)$localstore->check_lock_state($hashedkey, $localidentifier)); $this->assertEquals('b', $returnedinstance2->name); > $this->assertFalse((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier)); } > $this->assertTrue((bool)$localstore->check_lock_state($hashedkey, 'somebodyelse')); > } finally { public function test_identifiers_have_separate_caches() { > $localstore->release_lock($hashedkey, 'somebodyelse'); $cachepg = cache::make('core', 'databasemeta', array('dbfamily' => 'pgsql')); > } $cachepg->set(1, 'here'); > $cachemy = cache::make('core', 'databasemeta', array('dbfamily' => 'mysql')); > // 3. Just for completion, test what happens if we do lock it. $cachemy->set(2, 'there'); > $this->assertTrue($cache->acquire_lock('apple')); $this->assertEquals('here', $cachepg->get(1)); > try { $this->assertEquals('there', $cachemy->get(2)); > $this->assertTrue((bool)$localstore->check_lock_state($hashedkey, $localidentifier)); $this->assertFalse($cachemy->get(1)); > $this->assertTrue((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier)); } > } finally { > $cache->release_lock('apple'); public function test_performance_debug() { > } global $CFG; > } $this->resetAfterTest(true); > $CFG->perfdebug = 15; > /**$instance = cache_config_testing::instance(); $applicationid = 'phpunit/applicationperf'; $instance->phpunit_add_definition($applicationid, array( 'mode' => cache_store::MODE_APPLICATION, 'component' => 'phpunit', 'area' => 'applicationperf' )); $sessionid = 'phpunit/sessionperf'; $instance->phpunit_add_definition($sessionid, array( 'mode' => cache_store::MODE_SESSION, 'component' => 'phpunit', 'area' => 'sessionperf' )); $requestid = 'phpunit/requestperf'; $instance->phpunit_add_definition($requestid, array( 'mode' => cache_store::MODE_REQUEST, 'component' => 'phpunit', 'area' => 'requestperf' )); $application = cache::make('phpunit', 'applicationperf'); $session = cache::make('phpunit', 'sessionperf'); $request = cache::make('phpunit', 'requestperf'); // Check that no stats are recorded for these definitions yet. $stats = cache_helper::get_stats(); $this->assertArrayNotHasKey($applicationid, $stats); $this->assertArrayHasKey($sessionid, $stats); // Session cache sets a key on construct. $this->assertArrayNotHasKey($requestid, $stats); // Check that stores register misses. $this->assertFalse($application->get('missMe')); $this->assertFalse($application->get('missMe')); $this->assertFalse($session->get('missMe')); $this->assertFalse($session->get('missMe')); $this->assertFalse($session->get('missMe')); $this->assertFalse($request->get('missMe')); $this->assertFalse($request->get('missMe')); $this->assertFalse($request->get('missMe')); $this->assertFalse($request->get('missMe')); $endstats = cache_helper::get_stats(); $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['misses']); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits']); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets']); $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['misses']); $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits']); $this->assertEquals(1, $endstats[$sessionid]['stores']['default_session']['sets']); $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['misses']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets']); $startstats = cache_helper::get_stats(); // Check that stores register sets. $this->assertTrue($application->set('setMe1', 1)); $this->assertTrue($application->set('setMe2', 2)); $this->assertTrue($session->set('setMe1', 1)); $this->assertTrue($session->set('setMe2', 2)); $this->assertTrue($session->set('setMe3', 3)); $this->assertTrue($request->set('setMe1', 1)); $this->assertTrue($request->set('setMe2', 2)); $this->assertTrue($request->set('setMe3', 3)); $this->assertTrue($request->set('setMe4', 4)); $endstats = cache_helper::get_stats(); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - $startstats[$applicationid]['stores']['default_application']['misses']); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] - $startstats[$applicationid]['stores']['default_application']['hits']); $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] - $startstats[$applicationid]['stores']['default_application']['sets']); $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - $startstats[$sessionid]['stores']['default_session']['misses']); $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] - $startstats[$sessionid]['stores']['default_session']['hits']); $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] - $startstats[$sessionid]['stores']['default_session']['sets']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - $startstats[$requestid]['stores']['default_request']['misses']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] - $startstats[$requestid]['stores']['default_request']['hits']); $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] - $startstats[$requestid]['stores']['default_request']['sets']); $startstats = cache_helper::get_stats(); // Check that stores register hits. $this->assertEquals($application->get('setMe1'), 1); $this->assertEquals($application->get('setMe2'), 2); $this->assertEquals($session->get('setMe1'), 1); $this->assertEquals($session->get('setMe2'), 2); $this->assertEquals($session->get('setMe3'), 3); $this->assertEquals($request->get('setMe1'), 1); $this->assertEquals($request->get('setMe2'), 2); $this->assertEquals($request->get('setMe3'), 3); $this->assertEquals($request->get('setMe4'), 4); $endstats = cache_helper::get_stats(); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - $startstats[$applicationid]['stores']['default_application']['misses']); $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] - $startstats[$applicationid]['stores']['default_application']['hits']); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] - $startstats[$applicationid]['stores']['default_application']['sets']); $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - $startstats[$sessionid]['stores']['default_session']['misses']); $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] - $startstats[$sessionid]['stores']['default_session']['hits']); $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] - $startstats[$sessionid]['stores']['default_session']['sets']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - $startstats[$requestid]['stores']['default_request']['misses']); $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] - $startstats[$requestid]['stores']['default_request']['hits']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] - $startstats[$requestid]['stores']['default_request']['sets']); $startstats = cache_helper::get_stats(); // Check that stores register through get_many. $application->get_many(array('setMe1', 'setMe2')); $session->get_many(array('setMe1', 'setMe2', 'setMe3')); $request->get_many(array('setMe1', 'setMe2', 'setMe3', 'setMe4')); $endstats = cache_helper::get_stats(); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - $startstats[$applicationid]['stores']['default_application']['misses']); $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] - $startstats[$applicationid]['stores']['default_application']['hits']); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] - $startstats[$applicationid]['stores']['default_application']['sets']); $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - $startstats[$sessionid]['stores']['default_session']['misses']); $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] - $startstats[$sessionid]['stores']['default_session']['hits']); $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] - $startstats[$sessionid]['stores']['default_session']['sets']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - $startstats[$requestid]['stores']['default_request']['misses']); $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] - $startstats[$requestid]['stores']['default_request']['hits']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] - $startstats[$requestid]['stores']['default_request']['sets']); $startstats = cache_helper::get_stats(); // Check that stores register through set_many. $this->assertEquals(2, $application->set_many(['setMe1' => 1, 'setMe2' => 2])); $this->assertEquals(3, $session->set_many(['setMe1' => 1, 'setMe2' => 2, 'setMe3' => 3])); $this->assertEquals(4, $request->set_many(['setMe1' => 1, 'setMe2' => 2, 'setMe3' => 3, 'setMe4' => 4])); $endstats = cache_helper::get_stats(); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - $startstats[$applicationid]['stores']['default_application']['misses']); $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] - $startstats[$applicationid]['stores']['default_application']['hits']); $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] - $startstats[$applicationid]['stores']['default_application']['sets']); $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - $startstats[$sessionid]['stores']['default_session']['misses']); $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] - $startstats[$sessionid]['stores']['default_session']['hits']); $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] - $startstats[$sessionid]['stores']['default_session']['sets']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - $startstats[$requestid]['stores']['default_request']['misses']); $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] - $startstats[$requestid]['stores']['default_request']['hits']); $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] - $startstats[$requestid]['stores']['default_request']['sets']); }> > /** public function test_static_cache() { > * Data provider for static acceleration performance tests. global $CFG; > * $this->resetAfterTest(true); > * @return array $CFG->perfdebug = 15; > */ > public function static_acceleration_performance_provider(): array { // Create cache store with static acceleration. > // Note: These are the delta values, not the absolute values. $instance = cache_config_testing::instance(); > // Also note that the set will actually store the valuein the static cache immediately. $applicationid = 'phpunit/applicationperf'; > $validfirst = [ $instance->phpunit_add_definition($applicationid, array( > 'default_application' => [ 'mode' => cache_store::MODE_APPLICATION, > 'hits' => 1, 'component' => 'phpunit', > 'misses' => 0, 'area' => 'applicationperf', > ], 'simplekeys' => true, > cache_store::STATIC_ACCEL => [ 'staticacceleration' => true, > 'hits' => 0, 'staticaccelerationsize' => 3 > 'misses' => 1, )); > ], > ]; $application = cache::make('phpunit', 'applicationperf'); > > $validsecond = [ // Check that stores register sets. > 'default_application' => [ $this->assertTrue($application->set('setMe1', 1)); > 'hits' => 0, $this->assertTrue($application->set('setMe2', 0)); > 'misses' => 0, $this->assertTrue($application->set('setMe3', array())); > ], $this->assertTrue($application->get('setMe1') !== false); > cache_store::STATIC_ACCEL => [ $this->assertTrue($application->get('setMe2') !== false); > 'hits' => 1, $this->assertTrue($application->get('setMe3') !== false); > 'misses' => 0, > ], // Check that the static acceleration worked, even on empty arrays and the number 0. > ]; $endstats = cache_helper::get_stats(); > $this->assertEquals(0, $endstats[$applicationid]['stores']['** static accel. **']['misses']); > $invalidfirst = [ $this->assertEquals(3, $endstats[$applicationid]['stores']['** static accel. **']['hits']); > 'default_application' => [ } > 'hits' => 0, > 'misses' => 1, public function test_performance_debug_off() { > ], global $CFG; > cache_store::STATIC_ACCEL => [ $this->resetAfterTest(true); > 'hits' => 0, $CFG->perfdebug = 7; > 'misses' => 1, > ], $instance = cache_config_testing::instance(); > ]; $applicationid = 'phpunit/applicationperfoff'; > $invalidsecond = [ $instance->phpunit_add_definition($applicationid, array( > 'default_application' => [ 'mode' => cache_store::MODE_APPLICATION, > 'hits' => 0, 'component' => 'phpunit', > 'misses' => 1, 'area' => 'applicationperfoff' > ], )); > cache_store::STATIC_ACCEL => [ $sessionid = 'phpunit/sessionperfoff'; > 'hits' => 0, $instance->phpunit_add_definition($sessionid, array( > 'misses' => 1, 'mode' => cache_store::MODE_SESSION, > ], 'component' => 'phpunit', > ];; 'area' => 'sessionperfoff' > )); > return [ $requestid = 'phpunit/requestperfoff'; > 'Truthy' => [ $instance->phpunit_add_definition($requestid, array( > true, 'mode' => cache_store::MODE_REQUEST, > $validfirst, 'component' => 'phpunit', > $validsecond, 'area' => 'requestperfoff' > ], )); > 'Null' => [ > null, $application = cache::make('phpunit', 'applicationperfoff'); > $validfirst, $session = cache::make('phpunit', 'sessionperfoff'); > $validsecond, $request = cache::make('phpunit', 'requestperfoff'); > ], > 'Empty Array' => [ // Check that no stats are recorded for these definitions yet. > [], $stats = cache_helper::get_stats(); > $validfirst, $this->assertArrayNotHasKey($applicationid, $stats); > $validsecond, $this->assertArrayNotHasKey($sessionid, $stats); > ], $this->assertArrayNotHasKey($requestid, $stats); > 'Empty String' => [ > '', // Trigger cache misses, cache sets and cache hits. > $validfirst, $this->assertFalse($application->get('missMe')); > $validsecond, $this->assertTrue($application->set('setMe', 1)); > ], $this->assertEquals(1, $application->get('setMe')); > 'False' => [ $this->assertFalse($session->get('missMe')); > false, $this->assertTrue($session->set('setMe', 3)); > $invalidfirst, $this->assertEquals(3, $session->get('setMe')); > $invalidsecond, $this->assertFalse($request->get('missMe')); > ], $this->assertTrue($request->set('setMe', 4)); > ]; $this->assertEquals(4, $request->get('setMe')); > } > // Check that no stats are being recorded for these definitions. > /** $endstats = cache_helper::get_stats(); > * Test performance of static acceleration caches with values which are frequently confused with missing values. $this->assertArrayNotHasKey($applicationid, $endstats); > * $this->assertArrayNotHasKey($sessionid, $endstats); > * @dataProvider static_acceleration_performance_provider $this->assertArrayNotHasKey($requestid, $endstats); > * @param mixed $value The value to test } > * @param array $firstfetchstats The expected stats on the first fetch > * @param array $secondfetchstats The expected stats on the subsequent fetch /** > */ * Tests session cache event purge and subsequent visit in the same request. > public function test_static_acceleration_values_performance( * > $value, * This test simulates a cache being created, a value being set, then the value being purged. > array $firstfetchstats, * A subsequent use of the same cache is started in the same request which fills the cache. > array $secondfetchstats * A new request is started a short time later. > ): void { * The cache should be filled. > // Note: We need to modify perfdebug to test this. */ > global $CFG; public function test_session_event_purge_same_second() { > $this->resetAfterTest(true); $instance = cache_config_testing::instance(); > $CFG->perfdebug = 15; $instance->phpunit_add_definition('phpunit/eventpurgetest', array( > 'mode' => cache_store::MODE_SESSION, > $instance = cache_config_testing::instance(); 'component' => 'phpunit', > $instance->phpunit_add_definition('phpunit/accelerated', [ 'area' => 'eventpurgetest', > 'mode' => cache_store::MODE_APPLICATION, 'invalidationevents' => array( > 'component' => 'phpunit', 'crazyevent', > 'area' => 'accelerated', ) > 'staticacceleration' => true, )); > 'staticaccelerationsize' => 1, > ]); // Create the cache, set a value, and immediately purge it by event. > $cache = cache::make('phpunit', 'eventpurgetest'); > $cache = cache::make('phpunit', 'accelerated'); $cache->set('testkey1', 'test data 1'); > $this->assertInstanceOf(cache_phpunit_application::class, $cache); $this->assertEquals('test data 1', $cache->get('testkey1')); > cache_helper::purge_by_event('crazyevent'); > $this->assertTrue($cache->set('value', $value)); $this->assertFalse($cache->get('testkey1')); > > $checkstats = function( // Set up the cache again in the same request and add a new value back in. > array $start, $factory = cache_factory::instance(); > array $expectedstats $factory->reset_cache_instances(); > ): array { $cache = cache::make('phpunit', 'eventpurgetest'); > $applicationid = 'phpunit/accelerated'; $cache->set('testkey1', 'test data 2'); > $endstats = cache_helper::get_stats(); $this->assertEquals('test data 2', $cache->get('testkey1')); > > $start = $start[$applicationid]['stores']; // Trick the cache into thinking that this is a new request. > $end = $endstats[$applicationid]['stores']; cache_phpunit_cache::simulate_new_request(); > $factory = cache_factory::instance(); > foreach ($expectedstats as $cachename => $expected) { $factory->reset_cache_instances(); > foreach ($expected as $type => $value) { > $startvalue = array_key_exists($cachename, $start) ? $start[$cachename][$type] : 0; // Set up the cache again. > $endvalue = array_key_exists($cachename, $end) ? $end[$cachename][$type] : 0; // This is a subsequent request at a new time, so we instead the invalidation time will be checked. > $diff = $endvalue - $startvalue; // The invalidation time should match the last purged time and the cache will not be re-purged. > $this->assertEquals( $cache = cache::make('phpunit', 'eventpurgetest'); > $value, $this->assertEquals('test data 2', $cache->get('testkey1')); > $diff, } > "Expected $cachename $type to be $value, got $diff" > ); /** > } * Test that values set in different sessions are stored with different key prefixes. > } */ > public function test_session_distinct_storage_key() { > return $endstats; $this->resetAfterTest(); > }; > // Prepare a dummy session cache configuration. > // Reset the cache factory so that we can get the stats from a fresh instance. $config = cache_config_testing::instance(); > $factory = cache_factory::instance(); $config->phpunit_add_definition('phpunit/test_session_distinct_storage_key', array( > $factory->reset_cache_instances(); 'mode' => cache_store::MODE_SESSION, > $cache = cache::make('phpunit', 'accelerated'); 'component' => 'phpunit', > 'area' => 'test_session_distinct_storage_key' > // Get the initial stats. )); > $startstats = cache_helper::get_stats(); > // First anonymous user's session cache. > // Fetching the value the first time should seed the static cache from the application cache. cache_phpunit_session::phpunit_mockup_session_id('foo'); > $this->assertEquals($value, $cache->get('value')); $this->setUser(0); > $startstats = $checkstats($startstats, $firstfetchstats); $cache1 = cache::make('phpunit', 'test_session_distinct_storage_key'); > > // Fetching the value should only hit the static cache. // Reset cache instances to emulate a new request. > $this->assertEquals($value, $cache->get('value')); cache_factory::instance()->reset_cache_instances(); > $checkstats($startstats, $secondfetchstats); > } // Another anonymous user's session cache. >cache_phpunit_session::phpunit_mockup_session_id('bar'); $this->setUser(0); $cache2 = cache::make('phpunit', 'test_session_distinct_storage_key'); cache_factory::instance()->reset_cache_instances(); // Guest user's session cache. cache_phpunit_session::phpunit_mockup_session_id('baz'); $this->setGuestUser(); $cache3 = cache::make('phpunit', 'test_session_distinct_storage_key'); cache_factory::instance()->reset_cache_instances(); // Same guest user's session cache but in another browser window. cache_phpunit_session::phpunit_mockup_session_id('baz'); $this->setGuestUser(); $cache4 = cache::make('phpunit', 'test_session_distinct_storage_key'); // Assert that different PHP session implies different key prefix for storing values. $this->assertNotEquals($cache1->phpunit_get_key_prefix(), $cache2->phpunit_get_key_prefix()); // Assert that same PHP session implies same key prefix for storing values. $this->assertEquals($cache3->phpunit_get_key_prefix(), $cache4->phpunit_get_key_prefix()); } }