Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core_cache; 18 19 use cache; 20 use cache_application; 21 use cache_config; 22 use cache_config_disabled; 23 use cache_config_testing; 24 use cache_definition; 25 use cache_disabled; 26 use cache_factory; 27 use cache_factory_disabled; 28 use cache_helper; 29 use cache_loader; 30 use cache_phpunit_application; 31 use cache_phpunit_cache; 32 use cache_phpunit_dummy_object; 33 use cache_phpunit_dummy_overrideclass; 34 use cache_phpunit_factory; 35 use cache_phpunit_request; 36 use cache_phpunit_session; 37 use cache_request; 38 use cache_session; 39 use cache_store; 40 use cacheable_object_array; 41 42 /** 43 * PHPunit tests for the cache API 44 * 45 * This file is part of Moodle's cache API, affectionately called MUC. 46 * It contains the components that are requried in order to use caching. 47 * 48 * @package core 49 * @category cache 50 * @copyright 2012 Sam Hemelryk 51 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 52 * @coversDefaultClass \cache 53 * @covers \cache 54 */ 55 class cache_test extends \advanced_testcase { 56 57 /** 58 * Load required libraries and fixtures. 59 */ 60 public static function setUpBeforeClass(): void { 61 global $CFG; 62 63 require_once($CFG->dirroot . '/cache/locallib.php'); 64 require_once($CFG->dirroot . '/cache/tests/fixtures/lib.php'); 65 require_once($CFG->dirroot . '/cache/tests/fixtures/cache_phpunit_dummy_datasource_versionable.php'); 66 } 67 68 /** 69 * Set things back to the default before each test. 70 */ 71 public function setUp(): void { 72 parent::setUp(); 73 cache_factory::reset(); 74 cache_config_testing::create_default_configuration(); 75 } 76 77 /** 78 * Final task is to reset the cache system 79 */ 80 public static function tearDownAfterClass(): void { 81 parent::tearDownAfterClass(); 82 cache_factory::reset(); 83 } 84 85 /** 86 * Returns the expected application cache store. 87 * @return string 88 */ 89 protected function get_expected_application_cache_store() { 90 global $CFG; 91 $expected = 'cachestore_file'; 92 93 // Verify if we are using any of the available ways to use a different application store within tests. 94 if (defined('TEST_CACHE_USING_APPLICATION_STORE') && preg_match('#[a-zA-Z][a-zA-Z0-9_]*#', TEST_CACHE_USING_APPLICATION_STORE)) { 95 // 1st way. Using some of the testing servers. 96 $expected = 'cachestore_'.(string)TEST_CACHE_USING_APPLICATION_STORE; 97 98 } else if (defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH && !empty($CFG->altcacheconfigpath)) { 99 // 2nd way. Using an alternative configuration. 100 $defaultstores = cache_helper::get_stores_suitable_for_mode_default(); 101 $instance = cache_config::instance(); 102 // Iterate over defined mode mappings until we get an application one not being the default. 103 foreach ($instance->get_mode_mappings() as $mapping) { 104 // If the store is not for application mode, ignore. 105 if ($mapping['mode'] !== cache_store::MODE_APPLICATION) { 106 continue; 107 } 108 // If the store matches some default mapping store name, ignore. 109 if (array_key_exists($mapping['store'], $defaultstores) && !empty($defaultstores[$mapping['store']]['default'])) { 110 continue; 111 } 112 // Arrived here, have found an application mode store not being the default mapped one (file), 113 // that's the one we are using in the configuration for sure. 114 $expected = 'cachestore_'.$mapping['store']; 115 } 116 } 117 118 return $expected; 119 } 120 121 /** 122 * Tests cache configuration 123 */ 124 public function test_cache_config() { 125 global $CFG; 126 127 if (defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH && 128 !empty($CFG->altcacheconfigpath)) { 129 // We need to skip this test - it checks the default config structure, but very likely we arn't using the 130 // default config structure here so theres no point in running the test. 131 $this->markTestSkipped('Skipped testing default cache config structure as alt cache path is being used.'); 132 } 133 134 if (defined('TEST_CACHE_USING_APPLICATION_STORE')) { 135 // We need to skip this test - it checks the default config structure, but very likely we arn't using the 136 // default config structure here because we are testing against an alternative application store. 137 $this->markTestSkipped('Skipped testing default cache config structure as alt application store is being used.'); 138 } 139 140 $instance = cache_config::instance(); 141 $this->assertInstanceOf(cache_config_testing::class, $instance); 142 143 $this->assertTrue(cache_config_testing::config_file_exists()); 144 145 $stores = $instance->get_all_stores(); 146 $this->assertCount(3, $stores); 147 foreach ($stores as $name => $store) { 148 // Check its an array. 149 $this->assertIsArray($store); 150 // Check the name is the key. 151 $this->assertEquals($name, $store['name']); 152 // Check that it has been declared default. 153 $this->assertTrue($store['default']); 154 // Required attributes = name + plugin + configuration + modes + features. 155 $this->assertArrayHasKey('name', $store); 156 $this->assertArrayHasKey('plugin', $store); 157 $this->assertArrayHasKey('configuration', $store); 158 $this->assertArrayHasKey('modes', $store); 159 $this->assertArrayHasKey('features', $store); 160 } 161 162 $modemappings = $instance->get_mode_mappings(); 163 $this->assertCount(3, $modemappings); 164 $modes = array( 165 cache_store::MODE_APPLICATION => false, 166 cache_store::MODE_SESSION => false, 167 cache_store::MODE_REQUEST => false, 168 ); 169 foreach ($modemappings as $mapping) { 170 // We expect 3 properties. 171 $this->assertCount(3, $mapping); 172 // Required attributes = mode + store. 173 $this->assertArrayHasKey('mode', $mapping); 174 $this->assertArrayHasKey('store', $mapping); 175 // Record the mode. 176 $modes[$mapping['mode']] = true; 177 } 178 179 // Must have the default 3 modes and no more. 180 $this->assertCount(3, $mapping); 181 foreach ($modes as $mode) { 182 $this->assertTrue($mode); 183 } 184 185 $definitions = $instance->get_definitions(); 186 // The event invalidation definition is required for the cache API and must be there. 187 $this->assertArrayHasKey('core/eventinvalidation', $definitions); 188 189 $definitionmappings = $instance->get_definition_mappings(); 190 foreach ($definitionmappings as $mapping) { 191 // Required attributes = definition + store. 192 $this->assertArrayHasKey('definition', $mapping); 193 $this->assertArrayHasKey('store', $mapping); 194 } 195 } 196 197 /** 198 * Tests for cache keys that would break on windows. 199 */ 200 public function test_windows_nasty_keys() { 201 $instance = cache_config_testing::instance(); 202 $instance->phpunit_add_definition('phpunit/windowskeytest', array( 203 'mode' => cache_store::MODE_APPLICATION, 204 'component' => 'phpunit', 205 'area' => 'windowskeytest', 206 'simplekeys' => true, 207 'simpledata' => true 208 )); 209 $cache = cache::make('phpunit', 'windowskeytest'); 210 $this->assertTrue($cache->set('contest', 'test data 1')); 211 $this->assertEquals('test data 1', $cache->get('contest')); 212 } 213 214 /** 215 * Tests set_identifiers fails post cache creation. 216 * 217 * set_identifiers cannot be called after initial cache instantiation, as you need to create a difference cache. 218 */ 219 public function test_set_identifiers() { 220 $instance = cache_config_testing::instance(); 221 $instance->phpunit_add_definition('phpunit/identifier', array( 222 'mode' => cache_store::MODE_APPLICATION, 223 'component' => 'phpunit', 224 'area' => 'identifier', 225 'simplekeys' => true, 226 'simpledata' => true, 227 'staticacceleration' => true 228 )); 229 $cache = cache::make('phpunit', 'identifier', array('area')); 230 $this->assertTrue($cache->set('contest', 'test data 1')); 231 $this->assertEquals('test data 1', $cache->get('contest')); 232 233 $this->expectException('coding_exception'); 234 $cache->set_identifiers(array()); 235 } 236 237 /** 238 * Tests the default application cache 239 */ 240 public function test_default_application_cache() { 241 $cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'applicationtest'); 242 $this->assertInstanceOf(cache_application::class, $cache); 243 $this->run_on_cache($cache); 244 245 $instance = cache_config_testing::instance(true); 246 $instance->phpunit_add_definition('phpunit/test_default_application_cache', array( 247 'mode' => cache_store::MODE_APPLICATION, 248 'component' => 'phpunit', 249 'area' => 'test_default_application_cache', 250 'staticacceleration' => true, 251 'staticaccelerationsize' => 1 252 )); 253 $cache = cache::make('phpunit', 'test_default_application_cache'); 254 $this->assertInstanceOf(cache_application::class, $cache); 255 $this->run_on_cache($cache); 256 } 257 258 /** 259 * Tests the default session cache 260 */ 261 public function test_default_session_cache() { 262 $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'applicationtest'); 263 $this->assertInstanceOf(cache_session::class, $cache); 264 $this->run_on_cache($cache); 265 } 266 267 /** 268 * Tests the default request cache 269 */ 270 public function test_default_request_cache() { 271 $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'phpunit', 'applicationtest'); 272 $this->assertInstanceOf(cache_request::class, $cache); 273 $this->run_on_cache($cache); 274 } 275 276 /** 277 * Tests using a cache system when there are no stores available (who knows what the admin did to achieve this). 278 */ 279 public function test_on_cache_without_store() { 280 $instance = cache_config_testing::instance(true); 281 $instance->phpunit_add_definition('phpunit/nostoretest1', array( 282 'mode' => cache_store::MODE_APPLICATION, 283 'component' => 'phpunit', 284 'area' => 'nostoretest1', 285 )); 286 $instance->phpunit_add_definition('phpunit/nostoretest2', array( 287 'mode' => cache_store::MODE_APPLICATION, 288 'component' => 'phpunit', 289 'area' => 'nostoretest2', 290 'staticacceleration' => true 291 )); 292 $instance->phpunit_remove_stores(); 293 294 $cache = cache::make('phpunit', 'nostoretest1'); 295 $this->run_on_cache($cache); 296 297 $cache = cache::make('phpunit', 'nostoretest2'); 298 $this->run_on_cache($cache); 299 } 300 301 /** 302 * Runs a standard series of access and use tests on a cache instance. 303 * 304 * This function is great because we can use it to ensure all of the loaders perform exactly the same way. 305 * 306 * @param cache_loader $cache 307 */ 308 protected function run_on_cache(cache_loader $cache) { 309 $key = 'contestkey'; 310 $datascalars = array('test data', null); 311 $dataarray = array('contest' => 'data', 'part' => 'two'); 312 $dataobject = (object)$dataarray; 313 314 foreach ($datascalars as $datascalar) { 315 $this->assertTrue($cache->purge()); 316 317 // Check all read methods. 318 $this->assertFalse($cache->get($key)); 319 $this->assertFalse($cache->has($key)); 320 $result = $cache->get_many(array($key)); 321 $this->assertCount(1, $result); 322 $this->assertFalse(reset($result)); 323 $this->assertFalse($cache->has_any(array($key))); 324 $this->assertFalse($cache->has_all(array($key))); 325 326 // Set the data. 327 $this->assertTrue($cache->set($key, $datascalar)); 328 // Setting it more than once should be permitted. 329 $this->assertTrue($cache->set($key, $datascalar)); 330 331 // Recheck the read methods. 332 $this->assertEquals($datascalar, $cache->get($key)); 333 $this->assertTrue($cache->has($key)); 334 $result = $cache->get_many(array($key)); 335 $this->assertCount(1, $result); 336 $this->assertEquals($datascalar, reset($result)); 337 $this->assertTrue($cache->has_any(array($key))); 338 $this->assertTrue($cache->has_all(array($key))); 339 340 // Delete it. 341 $this->assertTrue($cache->delete($key)); 342 343 // Check its gone. 344 $this->assertFalse($cache->get($key)); 345 $this->assertFalse($cache->has($key)); 346 } 347 348 // Test arrays. 349 $this->assertTrue($cache->set($key, $dataarray)); 350 $this->assertEquals($dataarray, $cache->get($key)); 351 352 // Test objects. 353 $this->assertTrue($cache->set($key, $dataobject)); 354 $this->assertEquals($dataobject, $cache->get($key)); 355 356 $starttime = microtime(true); 357 $specobject = new cache_phpunit_dummy_object('red', 'blue', $starttime); 358 $this->assertTrue($cache->set($key, $specobject)); 359 $result = $cache->get($key); 360 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $result); 361 $this->assertEquals('red_ptc_wfc', $result->property1); 362 $this->assertEquals('blue_ptc_wfc', $result->property2); 363 $this->assertGreaterThan($starttime, $result->propertytime); 364 365 // Test array of objects. 366 $specobject = new cache_phpunit_dummy_object('red', 'blue', $starttime); 367 $data = new cacheable_object_array(array( 368 clone($specobject), 369 clone($specobject), 370 clone($specobject)) 371 ); 372 $this->assertTrue($cache->set($key, $data)); 373 $result = $cache->get($key); 374 $this->assertInstanceOf(cacheable_object_array::class, $result); 375 $this->assertCount(3, $data); 376 foreach ($result as $item) { 377 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $item); 378 $this->assertEquals('red_ptc_wfc', $item->property1); 379 $this->assertEquals('blue_ptc_wfc', $item->property2); 380 // Ensure that wake from cache is called in all cases. 381 $this->assertGreaterThan($starttime, $item->propertytime); 382 } 383 384 // Test set many. 385 $cache->set_many(array('key1' => 'data1', 'key2' => 'data2', 'key3' => null)); 386 $this->assertEquals('data1', $cache->get('key1')); 387 $this->assertEquals('data2', $cache->get('key2')); 388 $this->assertEquals(null, $cache->get('key3')); 389 $this->assertTrue($cache->delete('key1')); 390 $this->assertTrue($cache->delete('key2')); 391 $this->assertTrue($cache->delete('key3')); 392 393 $cache->set_many(array( 394 'key1' => array(1, 2, 3), 395 'key2' => array(3, 2, 1), 396 )); 397 $this->assertIsArray($cache->get('key1')); 398 $this->assertIsArray($cache->get('key2')); 399 $this->assertCount(3, $cache->get('key1')); 400 $this->assertCount(3, $cache->get('key2')); 401 $this->assertIsArray($cache->get_many(array('key1', 'key2'))); 402 $this->assertCount(2, $cache->get_many(array('key1', 'key2'))); 403 $this->assertEquals(2, $cache->delete_many(array('key1', 'key2'))); 404 405 // Test delete many. 406 $this->assertTrue($cache->set('key1', 'data1')); 407 $this->assertTrue($cache->set('key2', 'data2')); 408 $this->assertTrue($cache->set('key3', null)); 409 410 $this->assertEquals('data1', $cache->get('key1')); 411 $this->assertEquals('data2', $cache->get('key2')); 412 $this->assertEquals(null, $cache->get('key3')); 413 414 $this->assertEquals(3, $cache->delete_many(array('key1', 'key2', 'key3'))); 415 416 $this->assertFalse($cache->get('key1')); 417 $this->assertFalse($cache->get('key2')); 418 $this->assertFalse($cache->get('key3')); 419 420 // Quick reference test. 421 $obj = new \stdClass; 422 $obj->key = 'value'; 423 $ref =& $obj; 424 $this->assertTrue($cache->set('obj', $obj)); 425 426 $obj->key = 'eulav'; 427 $var = $cache->get('obj'); 428 $this->assertInstanceOf(\stdClass::class, $var); 429 $this->assertEquals('value', $var->key); 430 431 $ref->key = 'eulav'; 432 $var = $cache->get('obj'); 433 $this->assertInstanceOf(\stdClass::class, $var); 434 $this->assertEquals('value', $var->key); 435 436 $this->assertTrue($cache->delete('obj')); 437 438 // Deep reference test. 439 $obj1 = new \stdClass; 440 $obj1->key = 'value'; 441 $obj2 = new \stdClass; 442 $obj2->key = 'test'; 443 $obj3 = new \stdClass; 444 $obj3->key = 'pork'; 445 $obj1->subobj =& $obj2; 446 $obj2->subobj =& $obj3; 447 $this->assertTrue($cache->set('obj', $obj1)); 448 449 $obj1->key = 'eulav'; 450 $obj2->key = 'tset'; 451 $obj3->key = 'krop'; 452 $var = $cache->get('obj'); 453 $this->assertInstanceOf(\stdClass::class, $var); 454 $this->assertEquals('value', $var->key); 455 $this->assertInstanceOf(\stdClass::class, $var->subobj); 456 $this->assertEquals('test', $var->subobj->key); 457 $this->assertInstanceOf(\stdClass::class, $var->subobj->subobj); 458 $this->assertEquals('pork', $var->subobj->subobj->key); 459 $this->assertTrue($cache->delete('obj')); 460 461 // Death reference test... basically we don't want this to die. 462 $obj = new \stdClass; 463 $obj->key = 'value'; 464 $obj->self =& $obj; 465 $this->assertTrue($cache->set('obj', $obj)); 466 $var = $cache->get('obj'); 467 $this->assertInstanceOf(\stdClass::class, $var); 468 $this->assertEquals('value', $var->key); 469 470 // Reference test after retrieve. 471 $obj = new \stdClass; 472 $obj->key = 'value'; 473 $this->assertTrue($cache->set('obj', $obj)); 474 475 $var1 = $cache->get('obj'); 476 $this->assertInstanceOf(\stdClass::class, $var1); 477 $this->assertEquals('value', $var1->key); 478 $var1->key = 'eulav'; 479 $this->assertEquals('eulav', $var1->key); 480 481 $var2 = $cache->get('obj'); 482 $this->assertInstanceOf(\stdClass::class, $var2); 483 $this->assertEquals('value', $var2->key); 484 485 $this->assertTrue($cache->delete('obj')); 486 487 // Death reference test on get_many... basically we don't want this to die. 488 $obj = new \stdClass; 489 $obj->key = 'value'; 490 $obj->self =& $obj; 491 $this->assertEquals(1, $cache->set_many(array('obj' => $obj))); 492 $var = $cache->get_many(array('obj')); 493 $this->assertInstanceOf(\stdClass::class, $var['obj']); 494 $this->assertEquals('value', $var['obj']->key); 495 496 // Reference test after retrieve. 497 $obj = new \stdClass; 498 $obj->key = 'value'; 499 $this->assertEquals(1, $cache->set_many(array('obj' => $obj))); 500 501 $var1 = $cache->get_many(array('obj')); 502 $this->assertInstanceOf(\stdClass::class, $var1['obj']); 503 $this->assertEquals('value', $var1['obj']->key); 504 $var1['obj']->key = 'eulav'; 505 $this->assertEquals('eulav', $var1['obj']->key); 506 507 $var2 = $cache->get_many(array('obj')); 508 $this->assertInstanceOf(\stdClass::class, $var2['obj']); 509 $this->assertEquals('value', $var2['obj']->key); 510 511 $this->assertTrue($cache->delete('obj')); 512 513 // Test strictness exceptions. 514 try { 515 $cache->get('exception', MUST_EXIST); 516 $this->fail('Exception expected from cache::get using MUST_EXIST'); 517 } catch (\Exception $e) { 518 $this->assertTrue(true); 519 } 520 try { 521 $cache->get_many(array('exception1', 'exception2'), MUST_EXIST); 522 $this->fail('Exception expected from cache::get_many using MUST_EXIST'); 523 } catch (\Exception $e) { 524 $this->assertTrue(true); 525 } 526 $cache->set('test', 'test'); 527 try { 528 $cache->get_many(array('test', 'exception'), MUST_EXIST); 529 $this->fail('Exception expected from cache::get_many using MUST_EXIST'); 530 } catch (\Exception $e) { 531 $this->assertTrue(true); 532 } 533 } 534 535 /** 536 * Tests a definition using a data loader 537 */ 538 public function test_definition_data_loader() { 539 $instance = cache_config_testing::instance(true); 540 $instance->phpunit_add_definition('phpunit/datasourcetest', array( 541 'mode' => cache_store::MODE_APPLICATION, 542 'component' => 'phpunit', 543 'area' => 'datasourcetest', 544 'datasource' => 'cache_phpunit_dummy_datasource', 545 'datasourcefile' => 'cache/tests/fixtures/lib.php' 546 )); 547 548 $cache = cache::make('phpunit', 'datasourcetest'); 549 $this->assertInstanceOf(cache_application::class, $cache); 550 551 // Purge it to be sure. 552 $this->assertTrue($cache->purge()); 553 // It won't be there yet. 554 $this->assertFalse($cache->has('Test')); 555 // It should load it ;). 556 $this->assertTrue($cache->has('Test', true)); 557 558 // Purge it to be sure. 559 $this->assertTrue($cache->purge()); 560 $this->assertEquals('Test has no value really.', $cache->get('Test')); 561 562 // Test multiple values. 563 $this->assertTrue($cache->purge()); 564 $this->assertTrue($cache->set('b', 'B')); 565 $result = $cache->get_many(array('a', 'b', 'c')); 566 $this->assertIsArray($result); 567 $this->assertCount(3, $result); 568 $this->assertArrayHasKey('a', $result); 569 $this->assertArrayHasKey('b', $result); 570 $this->assertArrayHasKey('c', $result); 571 $this->assertEquals('a has no value really.', $result['a']); 572 $this->assertEquals('B', $result['b']); 573 $this->assertEquals('c has no value really.', $result['c']); 574 } 575 576 /** 577 * Tests a definition using a data loader with versioned keys. 578 * 579 * @covers ::get_versioned 580 * @covers ::set_versioned 581 */ 582 public function test_definition_data_loader_versioned() { 583 // Create two definitions, one using a non-versionable data source and the other using 584 // a versionable one. 585 $instance = cache_config_testing::instance(true); 586 $instance->phpunit_add_definition('phpunit/datasourcetest1', array( 587 'mode' => cache_store::MODE_APPLICATION, 588 'component' => 'phpunit', 589 'area' => 'datasourcetest1', 590 'datasource' => 'cache_phpunit_dummy_datasource', 591 'datasourcefile' => 'cache/tests/fixtures/lib.php' 592 )); 593 $instance->phpunit_add_definition('phpunit/datasourcetest2', array( 594 'mode' => cache_store::MODE_APPLICATION, 595 'component' => 'phpunit', 596 'area' => 'datasourcetest2', 597 'datasource' => 'cache_phpunit_dummy_datasource_versionable', 598 'datasourcefile' => 'cache/tests/fixtures/lib.php' 599 )); 600 601 // The first data source works for normal 'get'. 602 $cache1 = cache::make('phpunit', 'datasourcetest1'); 603 $this->assertEquals('Frog has no value really.', $cache1->get('Frog')); 604 605 // But it doesn't work for get_versioned. 606 try { 607 $cache1->get_versioned('zombie', 1); 608 $this->fail(); 609 } catch (\coding_exception $e) { 610 $this->assertStringContainsString('Data source is not versionable', $e->getMessage()); 611 } 612 613 // The second data source works for get_versioned. Set up the datasource first. 614 $cache2 = cache::make('phpunit', 'datasourcetest2'); 615 616 $datasource = \cache_phpunit_dummy_datasource_versionable::get_last_instance(); 617 $datasource->has_value('frog', 3, 'Kermit'); 618 619 // Check data with no value. 620 $this->assertFalse($cache2->get_versioned('zombie', 1)); 621 622 // Check data with value in datastore of required version. 623 $result = $cache2->get_versioned('frog', 3, IGNORE_MISSING, $actualversion); 624 $this->assertEquals('Kermit', $result); 625 $this->assertEquals(3, $actualversion); 626 627 // Check when the datastore doesn't have required version. 628 $this->assertFalse($cache2->get_versioned('frog', 4)); 629 } 630 631 /** 632 * Tests a definition using an overridden loader 633 */ 634 public function test_definition_overridden_loader() { 635 $instance = cache_config_testing::instance(true); 636 $instance->phpunit_add_definition('phpunit/overridetest', array( 637 'mode' => cache_store::MODE_APPLICATION, 638 'component' => 'phpunit', 639 'area' => 'overridetest', 640 'overrideclass' => 'cache_phpunit_dummy_overrideclass', 641 'overrideclassfile' => 'cache/tests/fixtures/lib.php' 642 )); 643 $cache = cache::make('phpunit', 'overridetest'); 644 $this->assertInstanceOf(cache_phpunit_dummy_overrideclass::class, $cache); 645 $this->assertInstanceOf(cache_application::class, $cache); 646 // Purge it to be sure. 647 $this->assertTrue($cache->purge()); 648 // It won't be there yet. 649 $this->assertFalse($cache->has('Test')); 650 // Add it. 651 $this->assertTrue($cache->set('Test', 'Test has no value really.')); 652 // Check its there. 653 $this->assertEquals('Test has no value really.', $cache->get('Test')); 654 } 655 656 /** 657 * Test the mappingsonly setting. 658 */ 659 public function test_definition_mappings_only() { 660 /** @var cache_config_testing $instance */ 661 $instance = cache_config_testing::instance(true); 662 $instance->phpunit_add_definition('phpunit/mappingsonly', array( 663 'mode' => cache_store::MODE_APPLICATION, 664 'component' => 'phpunit', 665 'area' => 'mappingsonly', 666 'mappingsonly' => true 667 ), false); 668 $instance->phpunit_add_definition('phpunit/nonmappingsonly', array( 669 'mode' => cache_store::MODE_APPLICATION, 670 'component' => 'phpunit', 671 'area' => 'nonmappingsonly', 672 'mappingsonly' => false 673 ), false); 674 675 $cacheonly = cache::make('phpunit', 'mappingsonly'); 676 $this->assertInstanceOf(cache_application::class, $cacheonly); 677 $this->assertEquals('cachestore_dummy', $cacheonly->phpunit_get_store_class()); 678 679 $expected = $this->get_expected_application_cache_store(); 680 $cachenon = cache::make('phpunit', 'nonmappingsonly'); 681 $this->assertInstanceOf(cache_application::class, $cachenon); 682 $this->assertEquals($expected, $cachenon->phpunit_get_store_class()); 683 } 684 685 /** 686 * Test a very basic definition. 687 */ 688 public function test_definition() { 689 $instance = cache_config_testing::instance(); 690 $instance->phpunit_add_definition('phpunit/test', array( 691 'mode' => cache_store::MODE_APPLICATION, 692 'component' => 'phpunit', 693 'area' => 'test', 694 )); 695 $cache = cache::make('phpunit', 'test'); 696 697 $this->assertTrue($cache->set('testkey1', 'test data 1')); 698 $this->assertEquals('test data 1', $cache->get('testkey1')); 699 $this->assertTrue($cache->set('testkey2', 'test data 2')); 700 $this->assertEquals('test data 2', $cache->get('testkey2')); 701 } 702 703 /** 704 * Test a definition using the simple keys. 705 */ 706 public function test_definition_simplekeys() { 707 $instance = cache_config_testing::instance(); 708 $instance->phpunit_add_definition('phpunit/simplekeytest', array( 709 'mode' => cache_store::MODE_APPLICATION, 710 'component' => 'phpunit', 711 'area' => 'simplekeytest', 712 'simplekeys' => true 713 )); 714 $cache = cache::make('phpunit', 'simplekeytest'); 715 716 $this->assertTrue($cache->set('testkey1', 'test data 1')); 717 $this->assertEquals('test data 1', $cache->get('testkey1')); 718 $this->assertTrue($cache->set('testkey2', 'test data 2')); 719 $this->assertEquals('test data 2', $cache->get('testkey2')); 720 721 $cache->purge(); 722 723 $this->assertTrue($cache->set('1', 'test data 1')); 724 $this->assertEquals('test data 1', $cache->get('1')); 725 $this->assertTrue($cache->set('2', 'test data 2')); 726 $this->assertEquals('test data 2', $cache->get('2')); 727 } 728 729 /** 730 * Test a negative TTL on an application cache. 731 */ 732 public function test_application_ttl_negative() { 733 $instance = cache_config_testing::instance(true); 734 $instance->phpunit_add_definition('phpunit/ttltest', array( 735 'mode' => cache_store::MODE_APPLICATION, 736 'component' => 'phpunit', 737 'area' => 'ttltest', 738 'ttl' => -86400 // Set to a day in the past to be extra sure. 739 )); 740 $cache = cache::make('phpunit', 'ttltest'); 741 $this->assertInstanceOf(cache_application::class, $cache); 742 743 // Purge it to be sure. 744 $this->assertTrue($cache->purge()); 745 // It won't be there yet. 746 $this->assertFalse($cache->has('Test')); 747 // Set it now. 748 $this->assertTrue($cache->set('Test', 'Test')); 749 // Check its not there. 750 $this->assertFalse($cache->has('Test')); 751 // Double check by trying to get it. 752 $this->assertFalse($cache->get('Test')); 753 754 // Test with multiple keys. 755 $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); 756 $result = $cache->get_many(array('a', 'b', 'c')); 757 $this->assertIsArray($result); 758 $this->assertCount(3, $result); 759 $this->assertArrayHasKey('a', $result); 760 $this->assertArrayHasKey('b', $result); 761 $this->assertArrayHasKey('c', $result); 762 $this->assertFalse($result['a']); 763 $this->assertFalse($result['b']); 764 $this->assertFalse($result['c']); 765 766 // Test with multiple keys including missing ones. 767 $result = $cache->get_many(array('a', 'c', 'e')); 768 $this->assertIsArray($result); 769 $this->assertCount(3, $result); 770 $this->assertArrayHasKey('a', $result); 771 $this->assertArrayHasKey('c', $result); 772 $this->assertArrayHasKey('e', $result); 773 $this->assertFalse($result['a']); 774 $this->assertFalse($result['c']); 775 $this->assertFalse($result['e']); 776 } 777 778 /** 779 * Test a positive TTL on an application cache. 780 */ 781 public function test_application_ttl_positive() { 782 $instance = cache_config_testing::instance(true); 783 $instance->phpunit_add_definition('phpunit/ttltest', array( 784 'mode' => cache_store::MODE_APPLICATION, 785 'component' => 'phpunit', 786 'area' => 'ttltest', 787 'ttl' => 86400 // Set to a day in the future to be extra sure. 788 )); 789 $cache = cache::make('phpunit', 'ttltest'); 790 $this->assertInstanceOf(cache_application::class, $cache); 791 792 // Purge it to be sure. 793 $this->assertTrue($cache->purge()); 794 // It won't be there yet. 795 $this->assertFalse($cache->has('Test')); 796 // Set it now. 797 $this->assertTrue($cache->set('Test', 'Test')); 798 // Check its there. 799 $this->assertTrue($cache->has('Test')); 800 // Double check by trying to get it. 801 $this->assertEquals('Test', $cache->get('Test')); 802 803 // Test with multiple keys. 804 $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); 805 $result = $cache->get_many(array('a', 'b', 'c')); 806 $this->assertIsArray($result); 807 $this->assertCount(3, $result); 808 $this->assertArrayHasKey('a', $result); 809 $this->assertArrayHasKey('b', $result); 810 $this->assertArrayHasKey('c', $result); 811 $this->assertEquals('A', $result['a']); 812 $this->assertEquals('B', $result['b']); 813 $this->assertEquals('C', $result['c']); 814 815 // Test with multiple keys including missing ones. 816 $result = $cache->get_many(array('a', 'c', 'e')); 817 $this->assertIsArray($result); 818 $this->assertCount(3, $result); 819 $this->assertArrayHasKey('a', $result); 820 $this->assertArrayHasKey('c', $result); 821 $this->assertArrayHasKey('e', $result); 822 $this->assertEquals('A', $result['a']); 823 $this->assertEquals('C', $result['c']); 824 $this->assertEquals(false, $result['e']); 825 } 826 827 /** 828 * Test a negative TTL on an session cache. 829 */ 830 public function test_session_ttl_positive() { 831 $instance = cache_config_testing::instance(true); 832 $instance->phpunit_add_definition('phpunit/ttltest', array( 833 'mode' => cache_store::MODE_SESSION, 834 'component' => 'phpunit', 835 'area' => 'ttltest', 836 'ttl' => 86400 // Set to a day in the future to be extra sure. 837 )); 838 $cache = cache::make('phpunit', 'ttltest'); 839 $this->assertInstanceOf(cache_session::class, $cache); 840 841 // Purge it to be sure. 842 $this->assertTrue($cache->purge()); 843 // It won't be there yet. 844 $this->assertFalse($cache->has('Test')); 845 // Set it now. 846 $this->assertTrue($cache->set('Test', 'Test')); 847 // Check its there. 848 $this->assertTrue($cache->has('Test')); 849 // Double check by trying to get it. 850 $this->assertEquals('Test', $cache->get('Test')); 851 852 // Test with multiple keys. 853 $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); 854 $result = $cache->get_many(array('a', 'b', 'c')); 855 $this->assertIsArray($result); 856 $this->assertCount(3, $result); 857 $this->assertArrayHasKey('a', $result); 858 $this->assertArrayHasKey('b', $result); 859 $this->assertArrayHasKey('c', $result); 860 $this->assertEquals('A', $result['a']); 861 $this->assertEquals('B', $result['b']); 862 $this->assertEquals('C', $result['c']); 863 864 // Test with multiple keys including missing ones. 865 $result = $cache->get_many(array('a', 'c', 'e')); 866 $this->assertIsArray($result); 867 $this->assertCount(3, $result); 868 $this->assertArrayHasKey('a', $result); 869 $this->assertArrayHasKey('c', $result); 870 $this->assertArrayHasKey('e', $result); 871 $this->assertEquals('A', $result['a']); 872 $this->assertEquals('C', $result['c']); 873 $this->assertEquals(false, $result['e']); 874 } 875 876 /** 877 * Tests manual locking operations on an application cache 878 */ 879 public function test_application_manual_locking() { 880 $instance = cache_config_testing::instance(); 881 $instance->phpunit_add_definition('phpunit/lockingtest', array( 882 'mode' => cache_store::MODE_APPLICATION, 883 'component' => 'phpunit', 884 'area' => 'lockingtest' 885 )); 886 // Configure the lock timeout so the test doesn't take too long to run. 887 $instance->phpunit_edit_store_config('default_application', ['lockwait' => 2]); 888 $cache1 = cache::make('phpunit', 'lockingtest'); 889 $cache2 = clone($cache1); 890 891 $this->assertTrue($cache1->set('testkey', 'test data')); 892 $this->assertTrue($cache2->set('testkey', 'test data')); 893 894 $cache1->acquire_lock('testkey'); 895 try { 896 $cache2->acquire_lock('testkey'); 897 $this->fail(); 898 } catch (\moodle_exception $e) { 899 // Check the right exception message, and debug info mentions the store type. 900 $this->assertMatchesRegularExpression('~Unable to acquire a lock.*cachestore_file.*~', 901 $e->getMessage()); 902 } 903 904 $this->assertTrue($cache1->check_lock_state('testkey')); 905 $this->assertFalse($cache2->check_lock_state('testkey')); 906 907 $this->assertTrue($cache1->release_lock('testkey')); 908 $this->assertFalse($cache2->release_lock('testkey')); 909 910 $this->assertTrue($cache1->set('testkey', 'test data')); 911 $this->assertTrue($cache2->set('testkey', 'test data')); 912 } 913 914 /** 915 * Tests application cache event invalidation 916 */ 917 public function test_application_event_invalidation() { 918 $instance = cache_config_testing::instance(); 919 $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 920 'mode' => cache_store::MODE_APPLICATION, 921 'component' => 'phpunit', 922 'area' => 'eventinvalidationtest', 923 'invalidationevents' => array( 924 'crazyevent' 925 ) 926 )); 927 $cache = cache::make('phpunit', 'eventinvalidationtest'); 928 929 $this->assertTrue($cache->set('testkey1', 'test data 1')); 930 $this->assertEquals('test data 1', $cache->get('testkey1')); 931 $this->assertTrue($cache->set('testkey2', 'test data 2')); 932 $this->assertEquals('test data 2', $cache->get('testkey2')); 933 934 // Test invalidating a single entry. 935 cache_helper::invalidate_by_event('crazyevent', array('testkey1')); 936 937 $this->assertFalse($cache->get('testkey1')); 938 $this->assertEquals('test data 2', $cache->get('testkey2')); 939 940 $this->assertTrue($cache->set('testkey1', 'test data 1')); 941 942 // Test invalidating both entries. 943 cache_helper::invalidate_by_event('crazyevent', array('testkey1', 'testkey2')); 944 945 $this->assertFalse($cache->get('testkey1')); 946 $this->assertFalse($cache->get('testkey2')); 947 } 948 949 /** 950 * Tests session cache event invalidation 951 */ 952 public function test_session_event_invalidation() { 953 $instance = cache_config_testing::instance(); 954 $instance->phpunit_add_definition('phpunit/test_session_event_invalidation', array( 955 'mode' => cache_store::MODE_SESSION, 956 'component' => 'phpunit', 957 'area' => 'test_session_event_invalidation', 958 'invalidationevents' => array( 959 'crazyevent' 960 ) 961 )); 962 $cache = cache::make('phpunit', 'test_session_event_invalidation'); 963 $this->assertInstanceOf(cache_session::class, $cache); 964 965 $this->assertTrue($cache->set('testkey1', 'test data 1')); 966 $this->assertEquals('test data 1', $cache->get('testkey1')); 967 $this->assertTrue($cache->set('testkey2', 'test data 2')); 968 $this->assertEquals('test data 2', $cache->get('testkey2')); 969 970 // Test invalidating a single entry. 971 cache_helper::invalidate_by_event('crazyevent', array('testkey1')); 972 973 $this->assertFalse($cache->get('testkey1')); 974 $this->assertEquals('test data 2', $cache->get('testkey2')); 975 976 $this->assertTrue($cache->set('testkey1', 'test data 1')); 977 978 // Test invalidating both entries. 979 cache_helper::invalidate_by_event('crazyevent', array('testkey1', 'testkey2')); 980 981 $this->assertFalse($cache->get('testkey1')); 982 $this->assertFalse($cache->get('testkey2')); 983 } 984 985 /** 986 * Tests application cache definition invalidation 987 */ 988 public function test_application_definition_invalidation() { 989 $instance = cache_config_testing::instance(); 990 $instance->phpunit_add_definition('phpunit/definitioninvalidation', array( 991 'mode' => cache_store::MODE_APPLICATION, 992 'component' => 'phpunit', 993 'area' => 'definitioninvalidation' 994 )); 995 $cache = cache::make('phpunit', 'definitioninvalidation'); 996 $this->assertTrue($cache->set('testkey1', 'test data 1')); 997 $this->assertEquals('test data 1', $cache->get('testkey1')); 998 $this->assertTrue($cache->set('testkey2', 'test data 2')); 999 $this->assertEquals('test data 2', $cache->get('testkey2')); 1000 1001 cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), 'testkey1'); 1002 1003 $this->assertFalse($cache->get('testkey1')); 1004 $this->assertEquals('test data 2', $cache->get('testkey2')); 1005 1006 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1007 1008 cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1')); 1009 1010 $this->assertFalse($cache->get('testkey1')); 1011 $this->assertEquals('test data 2', $cache->get('testkey2')); 1012 1013 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1014 1015 cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1', 'testkey2')); 1016 1017 $this->assertFalse($cache->get('testkey1')); 1018 $this->assertFalse($cache->get('testkey2')); 1019 } 1020 1021 /** 1022 * Tests session cache definition invalidation 1023 */ 1024 public function test_session_definition_invalidation() { 1025 $instance = cache_config_testing::instance(); 1026 $instance->phpunit_add_definition('phpunit/test_session_definition_invalidation', array( 1027 'mode' => cache_store::MODE_SESSION, 1028 'component' => 'phpunit', 1029 'area' => 'test_session_definition_invalidation' 1030 )); 1031 $cache = cache::make('phpunit', 'test_session_definition_invalidation'); 1032 $this->assertInstanceOf(cache_session::class, $cache); 1033 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1034 $this->assertEquals('test data 1', $cache->get('testkey1')); 1035 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1036 $this->assertEquals('test data 2', $cache->get('testkey2')); 1037 1038 cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), 'testkey1'); 1039 1040 $this->assertFalse($cache->get('testkey1')); 1041 $this->assertEquals('test data 2', $cache->get('testkey2')); 1042 1043 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1044 1045 cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), 1046 array('testkey1')); 1047 1048 $this->assertFalse($cache->get('testkey1')); 1049 $this->assertEquals('test data 2', $cache->get('testkey2')); 1050 1051 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1052 1053 cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), 1054 array('testkey1', 'testkey2')); 1055 1056 $this->assertFalse($cache->get('testkey1')); 1057 $this->assertFalse($cache->get('testkey2')); 1058 } 1059 1060 /** 1061 * Tests application cache event invalidation over a distributed setup. 1062 */ 1063 public function test_distributed_application_event_invalidation() { 1064 global $CFG; 1065 // This is going to be an intense wee test. 1066 // We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a 1067 // disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally 1068 // check that it is not picked up. 1069 $instance = cache_config_testing::instance(); 1070 $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 1071 'mode' => cache_store::MODE_APPLICATION, 1072 'component' => 'phpunit', 1073 'area' => 'eventinvalidationtest', 1074 'simplekeys' => true, 1075 'simpledata' => true, 1076 'invalidationevents' => array( 1077 'crazyevent' 1078 ) 1079 )); 1080 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1081 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1082 $this->assertEquals('test data 1', $cache->get('testkey1')); 1083 1084 cache_helper::invalidate_by_event('crazyevent', array('testkey1')); 1085 1086 $this->assertFalse($cache->get('testkey1')); 1087 1088 // OK data added, data invalidated, and invalidation time has been set. 1089 // Now we need to manually add back the data and adjust the invalidation time. 1090 $hash = md5(cache_store::MODE_APPLICATION.'/phpunit/eventinvalidationtest/'.$CFG->wwwroot.'phpunit'); 1091 $timefile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las-cache/lastinvalidation-$hash.cache"; 1092 // Make sure the file is correct. 1093 $this->assertTrue(file_exists($timefile)); 1094 $timecont = serialize(cache::now(true) - 60); // Back 60sec in the past to force it to re-invalidate. 1095 make_writable_directory(dirname($timefile)); 1096 file_put_contents($timefile, $timecont); 1097 $this->assertTrue(file_exists($timefile)); 1098 1099 $datafile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes-cache/testkey1-$hash.cache"; 1100 $datacont = serialize("test data 1"); 1101 make_writable_directory(dirname($datafile)); 1102 file_put_contents($datafile, $datacont); 1103 $this->assertTrue(file_exists($datafile)); 1104 1105 // Test 1: Rebuild without the event and test its there. 1106 cache_factory::reset(); 1107 $instance = cache_config_testing::instance(); 1108 $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 1109 'mode' => cache_store::MODE_APPLICATION, 1110 'component' => 'phpunit', 1111 'area' => 'eventinvalidationtest', 1112 'simplekeys' => true, 1113 'simpledata' => true, 1114 )); 1115 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1116 $this->assertEquals('test data 1', $cache->get('testkey1')); 1117 1118 // Test 2: Rebuild and test the invalidation of the event via the invalidation cache. 1119 cache_factory::reset(); 1120 1121 $instance = cache_config_testing::instance(); 1122 $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 1123 'mode' => cache_store::MODE_APPLICATION, 1124 'component' => 'phpunit', 1125 'area' => 'eventinvalidationtest', 1126 'simplekeys' => true, 1127 'simpledata' => true, 1128 'invalidationevents' => array( 1129 'crazyevent' 1130 ) 1131 )); 1132 1133 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1134 $this->assertFalse($cache->get('testkey1')); 1135 1136 // Test 3: Verify that an existing lastinvalidation cache file is updated when needed. 1137 1138 // Make a new cache class. This should should invalidate testkey2. 1139 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1140 1141 // Invalidation token should have been reset. 1142 $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); 1143 1144 // Set testkey2 data. 1145 $cache->set('testkey2', 'test data 2'); 1146 1147 // Backdate the event invalidation time by 30 seconds. 1148 $invalidationcache = cache::make('core', 'eventinvalidation'); 1149 $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 30)); 1150 1151 // Lastinvalidation should already be cache::now(). 1152 $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); 1153 1154 // Set it to 15 seconds ago so that we know if it changes. 1155 $pasttime = cache::now(true) - 15; 1156 $cache->set('lastinvalidation', $pasttime); 1157 1158 // Make a new cache class. This should not invalidate anything. 1159 cache_factory::instance()->reset_cache_instances(); 1160 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1161 1162 // Lastinvalidation shouldn't change since it was already newer than invalidation event. 1163 $this->assertEquals($pasttime, $cache->get('lastinvalidation')); 1164 1165 // Now set the event invalidation to newer than the lastinvalidation time. 1166 $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 5)); 1167 // Make a new cache class. This should should invalidate testkey2. 1168 cache_factory::instance()->reset_cache_instances(); 1169 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1170 // Lastinvalidation timestamp should have updated to cache::now(). 1171 $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); 1172 1173 // Now simulate a purge_by_event 5 seconds ago. 1174 $invalidationcache = cache::make('core', 'eventinvalidation'); 1175 $invalidationcache->set('crazyevent', array('purged' => cache::now(true) - 5)); 1176 // Set our lastinvalidation timestamp to 15 seconds ago. 1177 $cache->set('lastinvalidation', cache::now(true) - 15); 1178 // Make a new cache class. This should invalidate the cache. 1179 cache_factory::instance()->reset_cache_instances(); 1180 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1181 // Lastinvalidation timestamp should have updated to cache::now(). 1182 $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); 1183 1184 } 1185 1186 /** 1187 * Tests application cache event purge 1188 */ 1189 public function test_application_event_purge() { 1190 $instance = cache_config_testing::instance(); 1191 $instance->phpunit_add_definition('phpunit/eventpurgetest', array( 1192 'mode' => cache_store::MODE_APPLICATION, 1193 'component' => 'phpunit', 1194 'area' => 'eventpurgetest', 1195 'invalidationevents' => array( 1196 'crazyevent' 1197 ) 1198 )); 1199 $instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array( 1200 'mode' => cache_store::MODE_APPLICATION, 1201 'component' => 'phpunit', 1202 'area' => 'eventpurgetestaccelerated', 1203 'staticacceleration' => true, 1204 'invalidationevents' => array( 1205 'crazyevent' 1206 ) 1207 )); 1208 $cache = cache::make('phpunit', 'eventpurgetest'); 1209 1210 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1211 $this->assertEquals('test data 1', $cache->get('testkey1')); 1212 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1213 $this->assertEquals('test data 2', $cache->get('testkey2')); 1214 1215 // Purge the event. 1216 cache_helper::purge_by_event('crazyevent'); 1217 1218 // Check things have been removed. 1219 $this->assertFalse($cache->get('testkey1')); 1220 $this->assertFalse($cache->get('testkey2')); 1221 1222 // Now test the static acceleration array. 1223 $cache = cache::make('phpunit', 'eventpurgetestaccelerated'); 1224 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1225 $this->assertEquals('test data 1', $cache->get('testkey1')); 1226 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1227 $this->assertEquals('test data 2', $cache->get('testkey2')); 1228 1229 // Purge the event. 1230 cache_helper::purge_by_event('crazyevent'); 1231 1232 // Check things have been removed. 1233 $this->assertFalse($cache->get('testkey1')); 1234 $this->assertFalse($cache->get('testkey2')); 1235 } 1236 1237 /** 1238 * Tests session cache event purge 1239 */ 1240 public function test_session_event_purge() { 1241 $instance = cache_config_testing::instance(); 1242 $instance->phpunit_add_definition('phpunit/eventpurgetest', array( 1243 'mode' => cache_store::MODE_SESSION, 1244 'component' => 'phpunit', 1245 'area' => 'eventpurgetest', 1246 'invalidationevents' => array( 1247 'crazyevent' 1248 ) 1249 )); 1250 $instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array( 1251 'mode' => cache_store::MODE_SESSION, 1252 'component' => 'phpunit', 1253 'area' => 'eventpurgetestaccelerated', 1254 'staticacceleration' => true, 1255 'invalidationevents' => array( 1256 'crazyevent' 1257 ) 1258 )); 1259 $cache = cache::make('phpunit', 'eventpurgetest'); 1260 1261 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1262 $this->assertEquals('test data 1', $cache->get('testkey1')); 1263 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1264 $this->assertEquals('test data 2', $cache->get('testkey2')); 1265 1266 // Purge the event. 1267 cache_helper::purge_by_event('crazyevent'); 1268 1269 // Check things have been removed. 1270 $this->assertFalse($cache->get('testkey1')); 1271 $this->assertFalse($cache->get('testkey2')); 1272 1273 // Now test the static acceleration array. 1274 $cache = cache::make('phpunit', 'eventpurgetestaccelerated'); 1275 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1276 $this->assertEquals('test data 1', $cache->get('testkey1')); 1277 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1278 $this->assertEquals('test data 2', $cache->get('testkey2')); 1279 1280 // Purge the event. 1281 cache_helper::purge_by_event('crazyevent'); 1282 1283 // Check things have been removed. 1284 $this->assertFalse($cache->get('testkey1')); 1285 $this->assertFalse($cache->get('testkey2')); 1286 } 1287 1288 /** 1289 * Tests application cache definition purge 1290 */ 1291 public function test_application_definition_purge() { 1292 $instance = cache_config_testing::instance(); 1293 $instance->phpunit_add_definition('phpunit/definitionpurgetest', array( 1294 'mode' => cache_store::MODE_APPLICATION, 1295 'component' => 'phpunit', 1296 'area' => 'definitionpurgetest', 1297 'invalidationevents' => array( 1298 'crazyevent' 1299 ) 1300 )); 1301 $cache = cache::make('phpunit', 'definitionpurgetest'); 1302 1303 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1304 $this->assertEquals('test data 1', $cache->get('testkey1')); 1305 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1306 $this->assertEquals('test data 2', $cache->get('testkey2')); 1307 1308 // Purge the event. 1309 cache_helper::purge_by_definition('phpunit', 'definitionpurgetest'); 1310 1311 // Check things have been removed. 1312 $this->assertFalse($cache->get('testkey1')); 1313 $this->assertFalse($cache->get('testkey2')); 1314 } 1315 1316 /** 1317 * Test the use of an alt path. 1318 * If we can generate a config instance we are done :) 1319 */ 1320 public function test_alt_cache_path() { 1321 global $CFG; 1322 if ((defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) || !empty($CFG->altcacheconfigpath)) { 1323 $this->markTestSkipped('Skipped testing alt cache path as it is already being used.'); 1324 } 1325 $this->resetAfterTest(); 1326 $CFG->altcacheconfigpath = $CFG->dataroot.'/cache/altcacheconfigpath'; 1327 $instance = cache_config_testing::instance(); 1328 $this->assertInstanceOf(cache_config::class, $instance); 1329 } 1330 1331 /** 1332 * Test disabling the cache stores. 1333 */ 1334 public function test_disable_stores() { 1335 $instance = cache_config_testing::instance(); 1336 $instance->phpunit_add_definition('phpunit/disabletest1', array( 1337 'mode' => cache_store::MODE_APPLICATION, 1338 'component' => 'phpunit', 1339 'area' => 'disabletest1' 1340 )); 1341 $instance->phpunit_add_definition('phpunit/disabletest2', array( 1342 'mode' => cache_store::MODE_SESSION, 1343 'component' => 'phpunit', 1344 'area' => 'disabletest2' 1345 )); 1346 $instance->phpunit_add_definition('phpunit/disabletest3', array( 1347 'mode' => cache_store::MODE_REQUEST, 1348 'component' => 'phpunit', 1349 'area' => 'disabletest3' 1350 )); 1351 1352 $caches = array( 1353 'disabletest1' => cache::make('phpunit', 'disabletest1'), 1354 'disabletest2' => cache::make('phpunit', 'disabletest2'), 1355 'disabletest3' => cache::make('phpunit', 'disabletest3') 1356 ); 1357 1358 $this->assertInstanceOf(cache_phpunit_application::class, $caches['disabletest1']); 1359 $this->assertInstanceOf(cache_phpunit_session::class, $caches['disabletest2']); 1360 $this->assertInstanceOf(cache_phpunit_request::class, $caches['disabletest3']); 1361 1362 $this->assertEquals('cachestore_file', $caches['disabletest1']->phpunit_get_store_class()); 1363 $this->assertEquals('cachestore_session', $caches['disabletest2']->phpunit_get_store_class()); 1364 $this->assertEquals('cachestore_static', $caches['disabletest3']->phpunit_get_store_class()); 1365 1366 foreach ($caches as $cache) { 1367 $this->assertFalse($cache->get('test')); 1368 $this->assertTrue($cache->set('test', 'test')); 1369 $this->assertEquals('test', $cache->get('test')); 1370 } 1371 1372 cache_factory::disable_stores(); 1373 1374 $caches = array( 1375 'disabletest1' => cache::make('phpunit', 'disabletest1'), 1376 'disabletest2' => cache::make('phpunit', 'disabletest2'), 1377 'disabletest3' => cache::make('phpunit', 'disabletest3') 1378 ); 1379 1380 $this->assertInstanceOf(cache_phpunit_application::class, $caches['disabletest1']); 1381 $this->assertInstanceOf(cache_phpunit_session::class, $caches['disabletest2']); 1382 $this->assertInstanceOf(cache_phpunit_request::class, $caches['disabletest3']); 1383 1384 $this->assertEquals('cachestore_dummy', $caches['disabletest1']->phpunit_get_store_class()); 1385 $this->assertEquals('cachestore_dummy', $caches['disabletest2']->phpunit_get_store_class()); 1386 $this->assertEquals('cachestore_dummy', $caches['disabletest3']->phpunit_get_store_class()); 1387 1388 foreach ($caches as $cache) { 1389 $this->assertFalse($cache->get('test')); 1390 $this->assertTrue($cache->set('test', 'test')); 1391 $this->assertEquals('test', $cache->get('test')); 1392 } 1393 } 1394 1395 /** 1396 * Test disabling the cache. 1397 */ 1398 public function test_disable() { 1399 global $CFG; 1400 1401 if ((defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) || !empty($CFG->altcacheconfigpath)) { 1402 // We can't run this test as it requires us to delete the cache configuration script which we just 1403 // cant do with a custom path in play. 1404 $this->markTestSkipped('Skipped testing cache disable functionality as alt cache path is being used.'); 1405 } 1406 1407 $configfile = $CFG->dataroot.'/muc/config.php'; 1408 1409 // The config file will not exist yet as we've not done anything with the cache. 1410 // reset_all_data removes the file and without a call to create a configuration it doesn't exist 1411 // as yet. 1412 $this->assertFileDoesNotExist($configfile); 1413 1414 // Disable the cache 1415 cache_phpunit_factory::phpunit_disable(); 1416 1417 // Check we get the expected disabled factory. 1418 $factory = cache_factory::instance(); 1419 $this->assertInstanceOf(cache_factory_disabled::class, $factory); 1420 1421 // Check we get the expected disabled config. 1422 $config = $factory->create_config_instance(); 1423 $this->assertInstanceOf(cache_config_disabled::class, $config); 1424 1425 // Check we get the expected disabled caches. 1426 $cache = cache::make('core', 'string'); 1427 $this->assertInstanceOf(cache_disabled::class, $cache); 1428 1429 // Test an application cache. 1430 $cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'disable'); 1431 $this->assertInstanceOf(cache_disabled::class, $cache); 1432 1433 $this->assertFalse($cache->get('test')); 1434 $this->assertFalse($cache->get_versioned('v', 1)); 1435 $this->assertFalse($cache->set('test', 'test')); 1436 $this->assertFalse($cache->set_versioned('v', 1, 'data')); 1437 $this->assertFalse($cache->delete('test')); 1438 $this->assertTrue($cache->purge()); 1439 // Checking a lock should always report that we have one. 1440 // Acquiring or releasing a lock should always report success. 1441 $this->assertTrue($cache->check_lock_state('test')); 1442 $this->assertTrue($cache->acquire_lock('test')); 1443 $this->assertTrue($cache->acquire_lock('test')); 1444 $this->assertTrue($cache->check_lock_state('test')); 1445 $this->assertTrue($cache->release_lock('test')); 1446 $this->assertTrue($cache->release_lock('test')); 1447 $this->assertTrue($cache->check_lock_state('test')); 1448 1449 // Test a session cache. 1450 $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'disable'); 1451 $this->assertInstanceOf(cache_disabled::class, $cache); 1452 1453 $this->assertFalse($cache->get('test')); 1454 $this->assertFalse($cache->get_versioned('v', 1)); 1455 $this->assertFalse($cache->set('test', 'test')); 1456 $this->assertFalse($cache->set_versioned('v', 1, 'data')); 1457 $this->assertFalse($cache->delete('test')); 1458 $this->assertTrue($cache->purge()); 1459 1460 // Finally test a request cache. 1461 $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'phpunit', 'disable'); 1462 $this->assertInstanceOf(cache_disabled::class, $cache); 1463 1464 $this->assertFalse($cache->get('test')); 1465 $this->assertFalse($cache->get_versioned('v', 1)); 1466 $this->assertFalse($cache->set('test', 'test')); 1467 $this->assertFalse($cache->set_versioned('v', 1, 'data')); 1468 $this->assertFalse($cache->delete('test')); 1469 $this->assertTrue($cache->purge()); 1470 1471 cache_factory::reset(); 1472 1473 $factory = cache_factory::instance(true); 1474 $config = $factory->create_config_instance(); 1475 $this->assertEquals('cache_config_testing', get_class($config)); 1476 } 1477 1478 /** 1479 * Test that multiple application loaders work ok. 1480 */ 1481 public function test_multiple_application_loaders() { 1482 $instance = cache_config_testing::instance(true); 1483 $instance->phpunit_add_file_store('phpunittest1'); 1484 $instance->phpunit_add_file_store('phpunittest2'); 1485 $instance->phpunit_add_definition('phpunit/multi_loader', array( 1486 'mode' => cache_store::MODE_APPLICATION, 1487 'component' => 'phpunit', 1488 'area' => 'multi_loader' 1489 )); 1490 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3); 1491 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2); 1492 1493 $cache = cache::make('phpunit', 'multi_loader'); 1494 $this->assertInstanceOf(cache_application::class, $cache); 1495 $this->assertFalse($cache->get('test')); 1496 $this->assertTrue($cache->set('test', 'test')); 1497 $this->assertEquals('test', $cache->get('test')); 1498 $this->assertTrue($cache->delete('test')); 1499 $this->assertFalse($cache->get('test')); 1500 $this->assertTrue($cache->set('test', 'test')); 1501 $this->assertTrue($cache->purge()); 1502 $this->assertFalse($cache->get('test')); 1503 1504 // Test the many commands. 1505 $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); 1506 $result = $cache->get_many(array('a', 'b', 'c')); 1507 $this->assertIsArray($result); 1508 $this->assertCount(3, $result); 1509 $this->assertArrayHasKey('a', $result); 1510 $this->assertArrayHasKey('b', $result); 1511 $this->assertArrayHasKey('c', $result); 1512 $this->assertEquals('A', $result['a']); 1513 $this->assertEquals('B', $result['b']); 1514 $this->assertEquals('C', $result['c']); 1515 $this->assertEquals($result, $cache->get_many(array('a', 'b', 'c'))); 1516 $this->assertEquals(2, $cache->delete_many(array('a', 'c'))); 1517 $result = $cache->get_many(array('a', 'b', 'c')); 1518 $this->assertIsArray($result); 1519 $this->assertCount(3, $result); 1520 $this->assertArrayHasKey('a', $result); 1521 $this->assertArrayHasKey('b', $result); 1522 $this->assertArrayHasKey('c', $result); 1523 $this->assertFalse($result['a']); 1524 $this->assertEquals('B', $result['b']); 1525 $this->assertFalse($result['c']); 1526 1527 // Test non-recursive deletes. 1528 $this->assertTrue($cache->set('test', 'test')); 1529 $this->assertSame('test', $cache->get('test')); 1530 $this->assertTrue($cache->delete('test', false)); 1531 // We should still have it on a deeper loader. 1532 $this->assertSame('test', $cache->get('test')); 1533 // Test non-recusive with many functions. 1534 $this->assertSame(3, $cache->set_many(array( 1535 'one' => 'one', 1536 'two' => 'two', 1537 'three' => 'three' 1538 ))); 1539 $this->assertSame('one', $cache->get('one')); 1540 $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); 1541 $this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false)); 1542 $this->assertSame('one', $cache->get('one')); 1543 $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); 1544 } 1545 1546 /** 1547 * Data provider to try using a TTL or non-TTL cache. 1548 * 1549 * @return array 1550 */ 1551 public function ttl_or_not(): array { 1552 return [[false], [true]]; 1553 } 1554 1555 /** 1556 * Data provider to try using a TTL or non-TTL cache, and static acceleration or not. 1557 * 1558 * @return array 1559 */ 1560 public function ttl_and_static_acceleration_or_not(): array { 1561 return [[false, false], [false, true], [true, false], [true, true]]; 1562 } 1563 1564 /** 1565 * Data provider to try using a TTL or non-TTL cache, and simple data on or off. 1566 * 1567 * @return array 1568 */ 1569 public function ttl_and_simple_data_or_not(): array { 1570 // Same values as for ttl and static acceleration (two booleans). 1571 return $this->ttl_and_static_acceleration_or_not(); 1572 } 1573 1574 /** 1575 * Shared code to set up a two or three-layer versioned cache for testing. 1576 * 1577 * @param bool $ttl If true, sets TTL in the definition 1578 * @param bool $threelayer If true, uses a 3-layer instead of 2-layer cache 1579 * @param bool $staticacceleration If true, enables static acceleration 1580 * @param bool $simpledata If true, enables simple data 1581 * @return \cache_application Cache 1582 */ 1583 protected function create_versioned_cache(bool $ttl, bool $threelayer = false, 1584 bool $staticacceleration = false, bool $simpledata = false): \cache_application { 1585 $instance = cache_config_testing::instance(true); 1586 $instance->phpunit_add_file_store('a', false); 1587 $instance->phpunit_add_file_store('b', false); 1588 if ($threelayer) { 1589 $instance->phpunit_add_file_store('c', false); 1590 } 1591 $defarray = [ 1592 'mode' => cache_store::MODE_APPLICATION, 1593 'component' => 'phpunit', 1594 'area' => 'multi_loader' 1595 ]; 1596 if ($ttl) { 1597 $defarray['ttl'] = '600'; 1598 } 1599 if ($staticacceleration) { 1600 $defarray['staticacceleration'] = true; 1601 $defarray['staticaccelerationsize'] = 10; 1602 } 1603 if ($simpledata) { 1604 $defarray['simpledata'] = true; 1605 } 1606 $instance->phpunit_add_definition('phpunit/multi_loader', $defarray, false); 1607 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'a', 1); 1608 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'b', 2); 1609 if ($threelayer) { 1610 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'c', 3); 1611 } 1612 1613 $multicache = cache::make('phpunit', 'multi_loader'); 1614 return $multicache; 1615 } 1616 1617 /** 1618 * Tests basic use of versioned cache. 1619 * 1620 * @dataProvider ttl_and_simple_data_or_not 1621 * @param bool $ttl If true, uses a TTL cache. 1622 * @param bool $simpledata If true, turns on simple data flag 1623 * @covers ::set_versioned 1624 * @covers ::get_versioned 1625 */ 1626 public function test_versioned_cache_basic(bool $ttl, bool $simpledata): void { 1627 $multicache = $this->create_versioned_cache($ttl, false, false, $simpledata); 1628 1629 $this->assertTrue($multicache->set_versioned('game', 1, 'Pooh-sticks')); 1630 1631 $result = $multicache->get_versioned('game', 1, IGNORE_MISSING, $actualversion); 1632 $this->assertEquals('Pooh-sticks', $result); 1633 $this->assertEquals(1, $actualversion); 1634 } 1635 1636 /** 1637 * Tests versioned cache with objects. 1638 * 1639 * @dataProvider ttl_and_static_acceleration_or_not 1640 * @param bool $ttl If true, uses a TTL cache. 1641 * @param bool $staticacceleration If true, enables static acceleration 1642 * @covers ::set_versioned 1643 * @covers ::get_versioned 1644 */ 1645 public function test_versioned_cache_objects(bool $ttl, bool $staticacceleration): void { 1646 $multicache = $this->create_versioned_cache($ttl, false, $staticacceleration); 1647 1648 // Set an object value. 1649 $data = (object)['game' => 'Pooh-sticks']; 1650 $this->assertTrue($multicache->set_versioned('game', 1, $data)); 1651 1652 // Get it. 1653 $result = $multicache->get_versioned('game', 1); 1654 $this->assertEquals('Pooh-sticks', $result->game); 1655 1656 // Mess about with the value in the returned object. 1657 $result->game = 'Tag'; 1658 1659 // Get it again and confirm the cached object has not been affected. 1660 $result = $multicache->get_versioned('game', 1); 1661 $this->assertEquals('Pooh-sticks', $result->game); 1662 } 1663 1664 /** 1665 * Tests requesting a version that doesn't exist. 1666 * 1667 * @dataProvider ttl_or_not 1668 * @param bool $ttl If true, uses a TTL cache. 1669 * @covers ::set_versioned 1670 * @covers ::get_versioned 1671 */ 1672 public function test_versioned_cache_not_exist(bool $ttl): void { 1673 $multicache = $this->create_versioned_cache($ttl); 1674 1675 $multicache->set_versioned('game', 1, 'Pooh-sticks'); 1676 1677 // Exists but with wrong version. 1678 $this->assertFalse($multicache->get_versioned('game', 2)); 1679 1680 // Doesn't exist at all. 1681 $this->assertFalse($multicache->get_versioned('frog', 0)); 1682 } 1683 1684 /** 1685 * Tests attempts to use get after set_version or get_version after set. 1686 * 1687 * @dataProvider ttl_or_not 1688 * @param bool $ttl If true, uses a TTL cache. 1689 * @covers ::set_versioned 1690 * @covers ::get_versioned 1691 */ 1692 public function test_versioned_cache_incompatible_versioning(bool $ttl): void { 1693 $multicache = $this->create_versioned_cache($ttl); 1694 1695 // What if you use get on a get_version cache? 1696 $multicache->set_versioned('game', 1, 'Pooh-sticks'); 1697 try { 1698 $multicache->get('game'); 1699 $this->fail(); 1700 } catch (\coding_exception $e) { 1701 $this->assertStringContainsString('Unexpectedly found versioned cache entry', $e->getMessage()); 1702 } 1703 1704 // Or get_version on a get cache? 1705 $multicache->set('toy', 'Train set'); 1706 try { 1707 $multicache->get_versioned('toy', 1); 1708 $this->fail(); 1709 } catch (\coding_exception $e) { 1710 $this->assertStringContainsString('Unexpectedly found non-versioned cache entry', $e->getMessage()); 1711 } 1712 } 1713 1714 /** 1715 * Versions are only stored once, so if you set a newer version you will always get it even 1716 * if you ask for the lower version number. 1717 * 1718 * @dataProvider ttl_or_not 1719 * @param bool $ttl If true, uses a TTL cache. 1720 * @covers ::set_versioned 1721 * @covers ::get_versioned 1722 */ 1723 public function test_versioned_cache_single_copy(bool $ttl): void { 1724 $multicache = $this->create_versioned_cache($ttl); 1725 1726 $multicache->set_versioned('game', 1, 'Pooh-sticks'); 1727 $multicache->set_versioned('game', 2, 'Tag'); 1728 $this->assertEquals('Tag', $multicache->get_versioned('game', 1, IGNORE_MISSING, $actualversion)); 1729 1730 // The reported version number matches the one returned, not requested. 1731 $this->assertEquals(2, $actualversion); 1732 } 1733 1734 /** 1735 * If the first (local) store has an outdated copy but the second (shared) store has a newer 1736 * one, then it should automatically be retrieved. 1737 * 1738 * @dataProvider ttl_or_not 1739 * @param bool $ttl If true, uses a TTL cache. 1740 * @covers ::set_versioned 1741 * @covers ::get_versioned 1742 */ 1743 public function test_versioned_cache_outdated_local(bool $ttl): void { 1744 $multicache = $this->create_versioned_cache($ttl); 1745 1746 // Set initial value to version 2, 'Tag', in both stores. 1747 $multicache->set_versioned('game', 2, 'Tag'); 1748 1749 // Get the two separate cache stores for the multi-level cache. 1750 $factory = cache_factory::instance(); 1751 $definition = $factory->create_definition('phpunit', 'multi_loader'); 1752 [0 => $storea, 1 => $storeb] = $factory->get_store_instances_in_use($definition); 1753 1754 // Simulate what happens if the shared cache is updated with a new version but the 1755 // local one still has an old version. 1756 $hashgame = cache_helper::hash_key('game', $definition); 1757 $data = 'British Bulldog'; 1758 if ($ttl) { 1759 $data = new \cache_ttl_wrapper($data, 600); 1760 } 1761 $storeb->set($hashgame, new \core_cache\version_wrapper($data, 3)); 1762 1763 // If we ask for the old one we'll get it straight off from local cache. 1764 $this->assertEquals('Tag', $multicache->get_versioned('game', 2)); 1765 1766 // But if we ask for the new one it will still get it via the shared cache. 1767 $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 3)); 1768 1769 // Also, now it will have been updated in the local cache as well. 1770 $localvalue = $storea->get($hashgame); 1771 if ($ttl) { 1772 // In case the time has changed slightly since the first set, we can't do an exact 1773 // compare, so check it ignoring the time field. 1774 $this->assertEquals(3, $localvalue->version); 1775 $ttldata = $localvalue->data; 1776 $this->assertInstanceOf('cache_ttl_wrapper', $ttldata); 1777 $this->assertEquals('British Bulldog', $ttldata->data); 1778 } else { 1779 $this->assertEquals(new \core_cache\version_wrapper('British Bulldog', 3), $localvalue); 1780 } 1781 } 1782 1783 /** 1784 * When we request a newer version, older ones are automatically deleted in every level of the 1785 * cache (to save I/O if there are multiple requests, as if there is another request it will 1786 * not have to retrieve the values to find out that they're old). 1787 * 1788 * @dataProvider ttl_or_not 1789 * @param bool $ttl If true, uses a TTL cache. 1790 * @covers ::set_versioned 1791 * @covers ::get_versioned 1792 */ 1793 public function test_versioned_cache_deleting_outdated(bool $ttl): void { 1794 $multicache = $this->create_versioned_cache($ttl); 1795 1796 // Set initial value to version 2, 'Tag', in both stores. 1797 $multicache->set_versioned('game', 2, 'Tag'); 1798 1799 // Get the two separate cache stores for the multi-level cache. 1800 $factory = cache_factory::instance(); 1801 $definition = $factory->create_definition('phpunit', 'multi_loader'); 1802 [0 => $storea, 1 => $storeb] = $factory->get_store_instances_in_use($definition); 1803 1804 // If we request a newer version, then any older version should be deleted in each 1805 // cache level. 1806 $this->assertFalse($multicache->get_versioned('game', 4)); 1807 $hashgame = cache_helper::hash_key('game', $definition); 1808 $this->assertFalse($storea->get($hashgame)); 1809 $this->assertFalse($storeb->get($hashgame)); 1810 } 1811 1812 /** 1813 * Tests a versioned cache when using static cache. 1814 * 1815 * @covers ::set_versioned 1816 * @covers ::get_versioned 1817 */ 1818 public function test_versioned_cache_static(): void { 1819 $staticcache = $this->create_versioned_cache(false, false, true); 1820 1821 // Set a value in the cache, version 1. This will store it in static acceleration. 1822 $staticcache->set_versioned('game', 1, 'Pooh-sticks'); 1823 1824 // Get the first cache store (we don't need the second one for this test). 1825 $factory = cache_factory::instance(); 1826 $definition = $factory->create_definition('phpunit', 'multi_loader'); 1827 [0 => $storea] = $factory->get_store_instances_in_use($definition); 1828 1829 // Hack a newer version into cache store without directly calling set (now the static 1830 // has v1, store has v2). This simulates another client updating the cache. 1831 $hashgame = cache_helper::hash_key('game', $definition); 1832 $storea->set($hashgame, new \core_cache\version_wrapper('Tag', 2)); 1833 1834 // Get the key from the cache, v1. This will use static acceleration. 1835 $this->assertEquals('Pooh-sticks', $staticcache->get_versioned('game', 1)); 1836 1837 // Now if we ask for a newer version, it should not use the static cached one. 1838 $this->assertEquals('Tag', $staticcache->get_versioned('game', 2)); 1839 1840 // This get should have updated static acceleration, so it will be used next time without 1841 // a store request. 1842 $storea->set($hashgame, new \core_cache\version_wrapper('British Bulldog', 3)); 1843 $this->assertEquals('Tag', $staticcache->get_versioned('game', 2)); 1844 1845 // Requesting the higher version will get rid of static acceleration again. 1846 $this->assertEquals('British Bulldog', $staticcache->get_versioned('game', 3)); 1847 1848 // Finally ask for a version that doesn't exist anywhere, just to confirm it returns null. 1849 $this->assertFalse($staticcache->get_versioned('game', 4)); 1850 } 1851 1852 /** 1853 * Tests basic use of 3-layer versioned caches. 1854 * 1855 * @covers ::set_versioned 1856 * @covers ::get_versioned 1857 */ 1858 public function test_versioned_cache_3_layers_basic(): void { 1859 $multicache = $this->create_versioned_cache(false, true); 1860 1861 // Basic use of set_versioned and get_versioned. 1862 $multicache->set_versioned('game', 1, 'Pooh-sticks'); 1863 $this->assertEquals('Pooh-sticks', $multicache->get_versioned('game', 1)); 1864 1865 // What if you ask for a version that doesn't exist? 1866 $this->assertFalse($multicache->get_versioned('game', 2)); 1867 1868 // Setting a new version wipes out the old version; if you request it, you get the new one. 1869 $multicache->set_versioned('game', 2, 'Tag'); 1870 $this->assertEquals('Tag', $multicache->get_versioned('game', 1)); 1871 } 1872 1873 /** 1874 * Tests use of 3-layer versioned caches where the 3 layers currently have different versions. 1875 * 1876 * @covers ::set_versioned 1877 * @covers ::get_versioned 1878 */ 1879 public function test_versioned_cache_3_layers_different_data(): void { 1880 // Set version 2 using normal method. 1881 $multicache = $this->create_versioned_cache(false, true); 1882 $multicache->set_versioned('game', 2, 'Tag'); 1883 1884 // Get the three separate cache stores for the multi-level cache. 1885 $factory = cache_factory::instance(); 1886 $definition = $factory->create_definition('phpunit', 'multi_loader'); 1887 [0 => $storea, 1 => $storeb, 2 => $storec] = $factory->get_store_instances_in_use($definition); 1888 1889 // Set up two other versions so every level has a different version. 1890 $hashgame = cache_helper::hash_key('game', $definition); 1891 $storeb->set($hashgame, new \core_cache\version_wrapper('British Bulldog', 3)); 1892 $storec->set($hashgame, new \core_cache\version_wrapper('Hopscotch', 4)); 1893 1894 // First request can be satisfied from A; second request requires B... 1895 $this->assertEquals('Tag', $multicache->get_versioned('game', 2)); 1896 $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 3)); 1897 1898 // And should update the data in A. 1899 $this->assertEquals(new \core_cache\version_wrapper('British Bulldog', 3), $storea->get($hashgame)); 1900 $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 1)); 1901 1902 // But newer data should still be in C. 1903 $this->assertEquals('Hopscotch', $multicache->get_versioned('game', 4)); 1904 // Now it's stored in A and B too. 1905 $this->assertEquals(new \core_cache\version_wrapper('Hopscotch', 4), $storea->get($hashgame)); 1906 $this->assertEquals(new \core_cache\version_wrapper('Hopscotch', 4), $storeb->get($hashgame)); 1907 } 1908 1909 /** 1910 * Test that multiple application loaders work ok. 1911 */ 1912 public function test_multiple_session_loaders() { 1913 /* @var cache_config_testing $instance */ 1914 $instance = cache_config_testing::instance(true); 1915 $instance->phpunit_add_session_store('phpunittest1'); 1916 $instance->phpunit_add_session_store('phpunittest2'); 1917 $instance->phpunit_add_definition('phpunit/multi_loader', array( 1918 'mode' => cache_store::MODE_SESSION, 1919 'component' => 'phpunit', 1920 'area' => 'multi_loader' 1921 )); 1922 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3); 1923 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2); 1924 1925 $cache = cache::make('phpunit', 'multi_loader'); 1926 $this->assertInstanceOf(cache_session::class, $cache); 1927 $this->assertFalse($cache->get('test')); 1928 $this->assertTrue($cache->set('test', 'test')); 1929 $this->assertEquals('test', $cache->get('test')); 1930 $this->assertTrue($cache->delete('test')); 1931 $this->assertFalse($cache->get('test')); 1932 $this->assertTrue($cache->set('test', 'test')); 1933 $this->assertTrue($cache->purge()); 1934 $this->assertFalse($cache->get('test')); 1935 1936 // Test the many commands. 1937 $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); 1938 $result = $cache->get_many(array('a', 'b', 'c')); 1939 $this->assertIsArray($result); 1940 $this->assertCount(3, $result); 1941 $this->assertArrayHasKey('a', $result); 1942 $this->assertArrayHasKey('b', $result); 1943 $this->assertArrayHasKey('c', $result); 1944 $this->assertEquals('A', $result['a']); 1945 $this->assertEquals('B', $result['b']); 1946 $this->assertEquals('C', $result['c']); 1947 $this->assertEquals($result, $cache->get_many(array('a', 'b', 'c'))); 1948 $this->assertEquals(2, $cache->delete_many(array('a', 'c'))); 1949 $result = $cache->get_many(array('a', 'b', 'c')); 1950 $this->assertIsArray($result); 1951 $this->assertCount(3, $result); 1952 $this->assertArrayHasKey('a', $result); 1953 $this->assertArrayHasKey('b', $result); 1954 $this->assertArrayHasKey('c', $result); 1955 $this->assertFalse($result['a']); 1956 $this->assertEquals('B', $result['b']); 1957 $this->assertFalse($result['c']); 1958 1959 // Test non-recursive deletes. 1960 $this->assertTrue($cache->set('test', 'test')); 1961 $this->assertSame('test', $cache->get('test')); 1962 $this->assertTrue($cache->delete('test', false)); 1963 // We should still have it on a deeper loader. 1964 $this->assertSame('test', $cache->get('test')); 1965 // Test non-recusive with many functions. 1966 $this->assertSame(3, $cache->set_many(array( 1967 'one' => 'one', 1968 'two' => 'two', 1969 'three' => 'three' 1970 ))); 1971 $this->assertSame('one', $cache->get('one')); 1972 $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); 1973 $this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false)); 1974 $this->assertSame('one', $cache->get('one')); 1975 $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); 1976 } 1977 1978 /** 1979 * Test switching users with session caches. 1980 */ 1981 public function test_session_cache_switch_user() { 1982 $this->resetAfterTest(true); 1983 $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache'); 1984 $user1 = $this->getDataGenerator()->create_user(); 1985 $user2 = $this->getDataGenerator()->create_user(); 1986 1987 // Log in as the first user. 1988 $this->setUser($user1); 1989 $sesskey1 = sesskey(); 1990 1991 // Set a basic value in the cache. 1992 $cache->set('var', 1); 1993 $this->assertTrue($cache->has('var')); 1994 $this->assertEquals(1, $cache->get('var')); 1995 1996 // Change to the second user. 1997 $this->setUser($user2); 1998 $sesskey2 = sesskey(); 1999 2000 // Make sure the cache doesn't give us the data for the last user. 2001 $this->assertNotEquals($sesskey1, $sesskey2); 2002 $this->assertFalse($cache->has('var')); 2003 $this->assertEquals(false, $cache->get('var')); 2004 } 2005 2006 /** 2007 * Test switching users with session caches. 2008 */ 2009 public function test_session_cache_switch_user_application_mapping() { 2010 $this->resetAfterTest(true); 2011 $instance = cache_config_testing::instance(true); 2012 $instance->phpunit_add_file_store('testfilestore'); 2013 $instance->phpunit_add_definition('phpunit/testappsession', array( 2014 'mode' => cache_store::MODE_SESSION, 2015 'component' => 'phpunit', 2016 'area' => 'testappsession' 2017 )); 2018 $instance->phpunit_add_definition_mapping('phpunit/testappsession', 'testfilestore', 3); 2019 $cache = cache::make('phpunit', 'testappsession'); 2020 $user1 = $this->getDataGenerator()->create_user(); 2021 $user2 = $this->getDataGenerator()->create_user(); 2022 2023 // Log in as the first user. 2024 $this->setUser($user1); 2025 $sesskey1 = sesskey(); 2026 2027 // Set a basic value in the cache. 2028 $cache->set('var', 1); 2029 $this->assertTrue($cache->has('var')); 2030 $this->assertEquals(1, $cache->get('var')); 2031 2032 // Change to the second user. 2033 $this->setUser($user2); 2034 $sesskey2 = sesskey(); 2035 2036 // Make sure the cache doesn't give us the data for the last user. 2037 $this->assertNotEquals($sesskey1, $sesskey2); 2038 $this->assertFalse($cache->has('var')); 2039 $this->assertEquals(false, $cache->get('var')); 2040 } 2041 2042 /** 2043 * Test two session caches being used at once to confirm collisions don't occur. 2044 */ 2045 public function test_dual_session_caches() { 2046 $instance = cache_config_testing::instance(true); 2047 $instance->phpunit_add_definition('phpunit/testsess1', array( 2048 'mode' => cache_store::MODE_SESSION, 2049 'component' => 'phpunit', 2050 'area' => 'testsess1' 2051 )); 2052 $instance->phpunit_add_definition('phpunit/testsess2', array( 2053 'mode' => cache_store::MODE_SESSION, 2054 'component' => 'phpunit', 2055 'area' => 'testsess2' 2056 )); 2057 $cache1 = cache::make('phpunit', 'testsess1'); 2058 $cache2 = cache::make('phpunit', 'testsess2'); 2059 2060 $this->assertFalse($cache1->has('test')); 2061 $this->assertFalse($cache2->has('test')); 2062 2063 $this->assertTrue($cache1->set('test', '1')); 2064 2065 $this->assertTrue($cache1->has('test')); 2066 $this->assertFalse($cache2->has('test')); 2067 2068 $this->assertTrue($cache2->set('test', '2')); 2069 2070 $this->assertEquals(1, $cache1->get('test')); 2071 $this->assertEquals(2, $cache2->get('test')); 2072 2073 $this->assertTrue($cache1->delete('test')); 2074 } 2075 2076 /** 2077 * Test multiple session caches when switching user. 2078 */ 2079 public function test_session_cache_switch_user_multiple() { 2080 $this->resetAfterTest(true); 2081 $cache1 = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache1'); 2082 $cache2 = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache2'); 2083 $user1 = $this->getDataGenerator()->create_user(); 2084 $user2 = $this->getDataGenerator()->create_user(); 2085 2086 // Log in as the first user. 2087 $this->setUser($user1); 2088 $sesskey1 = sesskey(); 2089 2090 // Set a basic value in the caches. 2091 $cache1->set('var', 1); 2092 $cache2->set('var', 2); 2093 $this->assertEquals(1, $cache1->get('var')); 2094 $this->assertEquals(2, $cache2->get('var')); 2095 2096 // Change to the second user. 2097 $this->setUser($user2); 2098 $sesskey2 = sesskey(); 2099 2100 // Make sure the cache doesn't give us the data for the last user. 2101 // Also make sure that switching the user has lead to both caches being purged. 2102 $this->assertNotEquals($sesskey1, $sesskey2); 2103 $this->assertEquals(false, $cache1->get('var')); 2104 $this->assertEquals(false, $cache2->get('var')); 2105 } 2106 2107 /** 2108 * The application locking feature should work with caches that support multiple identifiers 2109 * (static cache and MongoDB with a specific setting). 2110 * 2111 * @covers \cache_application 2112 */ 2113 public function test_application_locking_multiple_identifier_cache() { 2114 // Get an arbitrary definition (modinfo). 2115 $instance = cache_config_testing::instance(true); 2116 $definitions = $instance->get_definitions(); 2117 $definition = \cache_definition::load('phpunit', $definitions['core/coursemodinfo']); 2118 2119 // Set up a static cache using that definition, wrapped in cache_application so we can do 2120 // locking. 2121 $store = new \cachestore_static('test'); 2122 $store->initialise($definition); 2123 $cache = new cache_application($definition, $store); 2124 2125 // Test the three locking functions. 2126 $cache->acquire_lock('frog'); 2127 $this->assertTrue($cache->check_lock_state('frog')); 2128 $cache->release_lock('frog'); 2129 } 2130 2131 /** 2132 * Test requiring a lock before attempting to set a key. 2133 * 2134 * @covers ::set_implementation 2135 */ 2136 public function test_application_locking_before_write() { 2137 $instance = cache_config_testing::instance(true); 2138 $instance->phpunit_add_definition('phpunit/test_application_locking', array( 2139 'mode' => cache_store::MODE_APPLICATION, 2140 'component' => 'phpunit', 2141 'area' => 'test_application_locking', 2142 'staticacceleration' => true, 2143 'staticaccelerationsize' => 1, 2144 'requirelockingbeforewrite' => true 2145 )); 2146 $cache = cache::make('phpunit', 'test_application_locking'); 2147 $this->assertInstanceOf(cache_application::class, $cache); 2148 2149 $cache->acquire_lock('a'); 2150 try { 2151 // Set with lock. 2152 $this->assertTrue($cache->set('a', 'A')); 2153 2154 // Set without lock. 2155 try { 2156 $cache->set('b', 'B'); 2157 $this->fail(); 2158 } catch (\coding_exception $e) { 2159 $this->assertStringContainsString( 2160 'Attempted to set cache key "b" without a lock. ' . 2161 'Locking before writes is required for phpunit/test_application_locking', 2162 $e->getMessage()); 2163 } 2164 2165 // Set many without full lock. 2166 try { 2167 $cache->set_many(['a' => 'AA', 'b' => 'BB']); 2168 $this->fail(); 2169 } catch (\coding_exception $e) { 2170 $this->assertStringContainsString( 2171 'Attempted to set cache key "b" without a lock.', 2172 $e->getMessage()); 2173 } 2174 2175 // Check it didn't set key a either. 2176 $this->assertEquals('A', $cache->get('a')); 2177 2178 // Set many with full lock. 2179 $cache->acquire_lock('b'); 2180 try { 2181 $this->assertEquals(2, $cache->set_many(['a' => 'AA', 'b' => 'BB'])); 2182 $this->assertEquals('AA', $cache->get('a')); 2183 $this->assertEquals('BB', $cache->get('b')); 2184 } finally { 2185 $cache->release_lock('b'); 2186 } 2187 2188 // Delete key with lock. 2189 $this->assertTrue($cache->delete('a')); 2190 $this->assertFalse($cache->get('a')); 2191 2192 // Delete key without lock. 2193 try { 2194 $cache->delete('b'); 2195 $this->fail(); 2196 } catch (\coding_exception $e) { 2197 $this->assertStringContainsString( 2198 'Attempted to delete cache key "b" without a lock.', 2199 $e->getMessage()); 2200 } 2201 2202 // Delete many without full lock. 2203 $cache->set('a', 'AAA'); 2204 try { 2205 $cache->delete_many(['a', 'b']); 2206 $this->fail(); 2207 } catch (\coding_exception $e) { 2208 $this->assertStringContainsString( 2209 'Attempted to delete cache key "b" without a lock.', 2210 $e->getMessage()); 2211 } 2212 // Nothing was deleted. 2213 $this->assertEquals('AAA', $cache->get('a')); 2214 2215 // Delete many with full lock. 2216 $cache->acquire_lock('b'); 2217 try { 2218 $this->assertEquals(2, $cache->delete_many(['a', 'b'])); 2219 } finally { 2220 $cache->release_lock('b'); 2221 } 2222 $this->assertFalse($cache->get('a')); 2223 $this->assertFalse($cache->get('b')); 2224 } finally { 2225 $cache->release_lock('a'); 2226 } 2227 } 2228 2229 /** 2230 * Test that locking before write works when writing across multiple layers. 2231 * 2232 * @covers \cache_loader 2233 * @return void 2234 */ 2235 public function test_application_locking_multiple_layers() { 2236 2237 $instance = cache_config_testing::instance(true); 2238 $instance->phpunit_add_definition('phpunit/test_application_locking', array( 2239 'mode' => cache_store::MODE_APPLICATION, 2240 'component' => 'phpunit', 2241 'area' => 'test_application_locking', 2242 'staticacceleration' => true, 2243 'staticaccelerationsize' => 1, 2244 'requirelockingbeforewrite' => true 2245 ), false); 2246 $instance->phpunit_add_file_store('phpunittest1'); 2247 $instance->phpunit_add_file_store('phpunittest2'); 2248 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest1', 1); 2249 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest2', 2); 2250 2251 $cache = cache::make('phpunit', 'test_application_locking'); 2252 $this->assertInstanceOf(cache_application::class, $cache); 2253 2254 // Check that we can set a key across multiple layers. 2255 $cache->acquire_lock('a'); 2256 $this->assertTrue($cache->set('a', 'A')); 2257 2258 // Delete from the current layer. 2259 $cache->delete('a', false); 2260 $cache->release_lock('a'); 2261 2262 // Check that we can get the value from the deeper layer, which will also re-set it in the current one. 2263 $this->assertEquals('A', $cache->get('a')); 2264 2265 // Try set/delete/get_many. 2266 $cache->acquire_lock('x'); 2267 $cache->acquire_lock('y'); 2268 $cache->acquire_lock('z'); 2269 $this->assertEquals(3, $cache->set_many(['x' => 'X', 'y' => 'Y', 'z' => 'Z'])); 2270 $cache->delete_many(['x', 'y', 'z'], false); 2271 $cache->release_lock('x'); 2272 $cache->release_lock('y'); 2273 $cache->release_lock('z'); 2274 2275 $this->assertEquals(['x' => 'X', 'y' => 'Y', 'z' => 'Z'], $cache->get_many(['x', 'y', 'z'])); 2276 2277 $cache->purge(); 2278 2279 // Try the tests again with a third layer. 2280 $instance->phpunit_add_file_store('phpunittest3'); 2281 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest3', 3); 2282 $cache = cache::make('phpunit', 'test_application_locking'); 2283 $this->assertInstanceOf(cache_application::class, $cache); 2284 2285 // Check that we can set a key across multiple layers. 2286 $cache->acquire_lock('a'); 2287 $this->assertTrue($cache->set('a', 'A')); 2288 2289 // Delete from the current layer. 2290 $cache->delete('a', false); 2291 $cache->release_lock('a'); 2292 2293 // Check that we can get the value from the deeper layer, which will also re-set it in the current one. 2294 $this->assertEquals('A', $cache->get('a')); 2295 2296 // Try set/delete/get_many. 2297 $cache->acquire_lock('x'); 2298 $cache->acquire_lock('y'); 2299 $cache->acquire_lock('z'); 2300 $this->assertEquals(3, $cache->set_many(['x' => 'X', 'y' => 'Y', 'z' => 'Z'])); 2301 $cache->delete_many(['x', 'y', 'z'], false); 2302 $cache->release_lock('x'); 2303 $cache->release_lock('y'); 2304 $cache->release_lock('z'); 2305 2306 $this->assertEquals(['x' => 'X', 'y' => 'Y', 'z' => 'Z'], $cache->get_many(['x', 'y', 'z'])); 2307 } 2308 2309 /** 2310 * Tests that locking fails correctly when either layer of a 2-layer cache has a lock already. 2311 * 2312 * @covers \cache_loader 2313 */ 2314 public function test_application_locking_multiple_layers_failures(): void { 2315 2316 $instance = cache_config_testing::instance(true); 2317 $instance->phpunit_add_definition('phpunit/test_application_locking', array( 2318 'mode' => cache_store::MODE_APPLICATION, 2319 'component' => 'phpunit', 2320 'area' => 'test_application_locking', 2321 'staticacceleration' => true, 2322 'staticaccelerationsize' => 1, 2323 'requirelockingbeforewrite' => true 2324 ), false); 2325 $instance->phpunit_add_file_store('phpunittest1'); 2326 $instance->phpunit_add_file_store('phpunittest2'); 2327 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest1', 1); 2328 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest2', 2); 2329 2330 $cache = cache::make('phpunit', 'test_application_locking'); 2331 2332 // We need to get the individual stores so as to set up the right behaviour here. 2333 $ref = new \ReflectionClass('\cache'); 2334 $definitionprop = $ref->getProperty('definition'); 2335 $definitionprop->setAccessible(true); 2336 $storeprop = $ref->getProperty('store'); 2337 $storeprop->setAccessible(true); 2338 $loaderprop = $ref->getProperty('loader'); 2339 $loaderprop->setAccessible(true); 2340 2341 $definition = $definitionprop->getValue($cache); 2342 $localstore = $storeprop->getValue($cache); 2343 $sharedcache = $loaderprop->getValue($cache); 2344 $sharedstore = $storeprop->getValue($sharedcache); 2345 2346 // Set the lock waiting time to 1 second so it doesn't take forever to run the test. 2347 $ref = new \ReflectionClass('\cachestore_file'); 2348 $lockwaitprop = $ref->getProperty('lockwait'); 2349 $lockwaitprop->setAccessible(true); 2350 2351 $lockwaitprop->setValue($localstore, 1); 2352 $lockwaitprop->setValue($sharedstore, 1); 2353 2354 // Get key details and the cache identifier. 2355 $hashedkey = cache_helper::hash_key('apple', $definition); 2356 $localidentifier = $cache->get_identifier(); 2357 $sharedidentifier = $sharedcache->get_identifier(); 2358 2359 // 1. Local cache is not locked but parent cache is locked. 2360 $sharedstore->acquire_lock($hashedkey, 'somebodyelse'); 2361 try { 2362 try { 2363 $cache->acquire_lock('apple'); 2364 $this->fail(); 2365 } catch (\moodle_exception $e) { 2366 } 2367 2368 // Neither store is locked by us, shared store still locked. 2369 $this->assertFalse((bool)$localstore->check_lock_state($hashedkey, $localidentifier)); 2370 $this->assertFalse((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier)); 2371 $this->assertTrue((bool)$sharedstore->check_lock_state($hashedkey, 'somebodyelse')); 2372 } finally { 2373 $sharedstore->release_lock($hashedkey, 'somebodyelse'); 2374 } 2375 2376 // 2. Local cache is locked, parent cache is not locked. 2377 $localstore->acquire_lock($hashedkey, 'somebodyelse'); 2378 try { 2379 try { 2380 $cache->acquire_lock('apple'); 2381 $this->fail(); 2382 } catch (\moodle_exception $e) { 2383 2384 } 2385 2386 // Neither store is locked by us, local store still locked. 2387 $this->assertFalse((bool)$localstore->check_lock_state($hashedkey, $localidentifier)); 2388 $this->assertFalse((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier)); 2389 $this->assertTrue((bool)$localstore->check_lock_state($hashedkey, 'somebodyelse')); 2390 } finally { 2391 $localstore->release_lock($hashedkey, 'somebodyelse'); 2392 } 2393 2394 // 3. Just for completion, test what happens if we do lock it. 2395 $this->assertTrue($cache->acquire_lock('apple')); 2396 try { 2397 $this->assertTrue((bool)$localstore->check_lock_state($hashedkey, $localidentifier)); 2398 $this->assertTrue((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier)); 2399 } finally { 2400 $cache->release_lock('apple'); 2401 } 2402 } 2403 2404 /** 2405 * Test the static cache_helper method purge_stores_used_by_definition. 2406 */ 2407 public function test_purge_stores_used_by_definition() { 2408 $instance = cache_config_testing::instance(true); 2409 $instance->phpunit_add_definition('phpunit/test_purge_stores_used_by_definition', array( 2410 'mode' => cache_store::MODE_APPLICATION, 2411 'component' => 'phpunit', 2412 'area' => 'test_purge_stores_used_by_definition' 2413 )); 2414 $cache = cache::make('phpunit', 'test_purge_stores_used_by_definition'); 2415 $this->assertInstanceOf(cache_application::class, $cache); 2416 $this->assertTrue($cache->set('test', 'test')); 2417 unset($cache); 2418 2419 cache_helper::purge_stores_used_by_definition('phpunit', 'test_purge_stores_used_by_definition'); 2420 2421 $cache = cache::make('phpunit', 'test_purge_stores_used_by_definition'); 2422 $this->assertInstanceOf(cache_application::class, $cache); 2423 $this->assertFalse($cache->get('test')); 2424 } 2425 2426 /** 2427 * Test purge routines. 2428 */ 2429 public function test_purge_routines() { 2430 $instance = cache_config_testing::instance(true); 2431 $instance->phpunit_add_definition('phpunit/purge1', array( 2432 'mode' => cache_store::MODE_APPLICATION, 2433 'component' => 'phpunit', 2434 'area' => 'purge1' 2435 )); 2436 $instance->phpunit_add_definition('phpunit/purge2', array( 2437 'mode' => cache_store::MODE_APPLICATION, 2438 'component' => 'phpunit', 2439 'area' => 'purge2', 2440 'requireidentifiers' => array( 2441 'id' 2442 ) 2443 )); 2444 2445 $factory = cache_factory::instance(); 2446 $definition = $factory->create_definition('phpunit', 'purge1'); 2447 $this->assertFalse($definition->has_required_identifiers()); 2448 $cache = $factory->create_cache($definition); 2449 $this->assertInstanceOf(cache_application::class, $cache); 2450 $this->assertTrue($cache->set('test', 'test')); 2451 $this->assertTrue($cache->has('test')); 2452 cache_helper::purge_by_definition('phpunit', 'purge1'); 2453 $this->assertFalse($cache->has('test')); 2454 2455 $factory = cache_factory::instance(); 2456 $definition = $factory->create_definition('phpunit', 'purge2'); 2457 $this->assertTrue($definition->has_required_identifiers()); 2458 $cache = $factory->create_cache($definition); 2459 $this->assertInstanceOf(cache_application::class, $cache); 2460 $this->assertTrue($cache->set('test', 'test')); 2461 $this->assertTrue($cache->has('test')); 2462 cache_helper::purge_stores_used_by_definition('phpunit', 'purge2'); 2463 $this->assertFalse($cache->has('test')); 2464 2465 try { 2466 cache_helper::purge_by_definition('phpunit', 'purge2'); 2467 $this->fail('Should not be able to purge a definition required identifiers without providing them.'); 2468 } catch (\coding_exception $ex) { 2469 $this->assertStringContainsString('Identifier required for cache has not been provided', $ex->getMessage()); 2470 } 2471 } 2472 2473 /** 2474 * Tests that ad-hoc caches are correctly purged with a purge_all call. 2475 */ 2476 public function test_purge_all_with_adhoc_caches() { 2477 $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'core_cache', 'test'); 2478 $cache->set('test', 123); 2479 cache_helper::purge_all(); 2480 $this->assertFalse($cache->get('test')); 2481 } 2482 2483 /** 2484 * Test that the default stores all support searching. 2485 */ 2486 public function test_defaults_support_searching() { 2487 $instance = cache_config_testing::instance(true); 2488 $instance->phpunit_add_definition('phpunit/search1', array( 2489 'mode' => cache_store::MODE_APPLICATION, 2490 'component' => 'phpunit', 2491 'area' => 'search1', 2492 'requiresearchable' => true 2493 )); 2494 $instance->phpunit_add_definition('phpunit/search2', array( 2495 'mode' => cache_store::MODE_SESSION, 2496 'component' => 'phpunit', 2497 'area' => 'search2', 2498 'requiresearchable' => true 2499 )); 2500 $instance->phpunit_add_definition('phpunit/search3', array( 2501 'mode' => cache_store::MODE_REQUEST, 2502 'component' => 'phpunit', 2503 'area' => 'search3', 2504 'requiresearchable' => true 2505 )); 2506 $factory = cache_factory::instance(); 2507 2508 // Test application cache is searchable. 2509 $definition = $factory->create_definition('phpunit', 'search1'); 2510 $this->assertInstanceOf(cache_definition::class, $definition); 2511 $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); 2512 $cache = $factory->create_cache($definition); 2513 $this->assertInstanceOf(cache_application::class, $cache); 2514 $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); 2515 2516 // Test session cache is searchable. 2517 $definition = $factory->create_definition('phpunit', 'search2'); 2518 $this->assertInstanceOf(cache_definition::class, $definition); 2519 $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); 2520 $cache = $factory->create_cache($definition); 2521 $this->assertInstanceOf(cache_session::class, $cache); 2522 $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); 2523 2524 // Test request cache is searchable. 2525 $definition = $factory->create_definition('phpunit', 'search3'); 2526 $this->assertInstanceOf(cache_definition::class, $definition); 2527 $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); 2528 $cache = $factory->create_cache($definition); 2529 $this->assertInstanceOf(cache_request::class, $cache); 2530 $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); 2531 } 2532 2533 /** 2534 * Test static acceleration 2535 * 2536 * Note: All the assertGreaterThanOrEqual() in this test should be assertGreaterThan() be because of some microtime() 2537 * resolution problems under some OSs / PHP versions, we are accepting equal as valid outcome. For more info see MDL-57147. 2538 */ 2539 public function test_static_acceleration() { 2540 $instance = cache_config_testing::instance(); 2541 $instance->phpunit_add_definition('phpunit/accelerated', array( 2542 'mode' => cache_store::MODE_APPLICATION, 2543 'component' => 'phpunit', 2544 'area' => 'accelerated', 2545 'staticacceleration' => true, 2546 'staticaccelerationsize' => 3, 2547 )); 2548 $instance->phpunit_add_definition('phpunit/accelerated2', array( 2549 'mode' => cache_store::MODE_APPLICATION, 2550 'component' => 'phpunit', 2551 'area' => 'accelerated2', 2552 'staticacceleration' => true, 2553 'staticaccelerationsize' => 3, 2554 )); 2555 $instance->phpunit_add_definition('phpunit/accelerated3', array( 2556 'mode' => cache_store::MODE_APPLICATION, 2557 'component' => 'phpunit', 2558 'area' => 'accelerated3', 2559 'staticacceleration' => true, 2560 'staticaccelerationsize' => 3, 2561 )); 2562 $instance->phpunit_add_definition('phpunit/accelerated4', array( 2563 'mode' => cache_store::MODE_APPLICATION, 2564 'component' => 'phpunit', 2565 'area' => 'accelerated4', 2566 'staticacceleration' => true, 2567 'staticaccelerationsize' => 4, 2568 )); 2569 $instance->phpunit_add_definition('phpunit/simpledataarea1', array( 2570 'mode' => cache_store::MODE_APPLICATION, 2571 'component' => 'phpunit', 2572 'area' => 'simpledataarea1', 2573 'staticacceleration' => true, 2574 'simpledata' => false 2575 )); 2576 $instance->phpunit_add_definition('phpunit/simpledataarea2', array( 2577 'mode' => cache_store::MODE_APPLICATION, 2578 'component' => 'phpunit', 2579 'area' => 'simpledataarea2', 2580 'staticacceleration' => true, 2581 'simpledata' => true 2582 )); 2583 2584 $cache = cache::make('phpunit', 'accelerated'); 2585 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 2586 2587 // Set and get three elements. 2588 $this->assertTrue($cache->set('a', 'A')); 2589 $this->assertTrue($cache->set('b', 'B')); 2590 $this->assertTrue($cache->set('c', 'C')); 2591 $this->assertEquals('A', $cache->get('a')); 2592 $this->assertEquals(array('b' => 'B', 'c' => 'C'), $cache->get_many(array('b', 'c'))); 2593 2594 // Make sure all items are in static acceleration array. 2595 $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); 2596 $this->assertEquals('B', $cache->phpunit_static_acceleration_get('b')); 2597 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2598 2599 // Add new value and make sure it is in cache and it is in array. 2600 $this->assertTrue($cache->set('d', 'D')); 2601 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2602 $this->assertEquals('D', $cache->get('d')); 2603 2604 // Now the least recent accessed item (a) is no longer in acceleration array. 2605 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2606 $this->assertEquals('B', $cache->phpunit_static_acceleration_get('b')); 2607 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2608 2609 // Adding and deleting element. 2610 $this->assertTrue($cache->set('a', 'A')); 2611 $this->assertTrue($cache->delete('a')); 2612 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2613 $this->assertFalse($cache->has('a')); 2614 2615 // Make sure "purge" deletes from the array as well. 2616 $cache->purge(); 2617 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2618 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2619 $this->assertFalse($cache->phpunit_static_acceleration_get('c')); 2620 $this->assertFalse($cache->phpunit_static_acceleration_get('d')); 2621 $this->assertFalse($cache->phpunit_static_acceleration_get('e')); 2622 2623 // Check that the array holds the last accessed items by get/set. 2624 $this->assertTrue($cache->set('a', 'A')); 2625 $this->assertTrue($cache->set('b', 'B')); 2626 $this->assertTrue($cache->set('c', 'C')); 2627 $this->assertTrue($cache->set('d', 'D')); 2628 $this->assertTrue($cache->set('e', 'E')); 2629 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2630 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2631 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2632 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2633 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2634 2635 // Store a cacheable_object, get many times and ensure each time wake_for_cache is used. 2636 // Both get and get_many are tested. Two cache entries are used to ensure the times aren't 2637 // confused with multiple calls to get()/get_many(). 2638 $startmicrotime = microtime(true); 2639 $cacheableobject = new cache_phpunit_dummy_object(1, 1, $startmicrotime); 2640 $cacheableobject2 = new cache_phpunit_dummy_object(2, 2, $startmicrotime); 2641 $this->assertTrue($cache->set('a', $cacheableobject)); 2642 $this->assertTrue($cache->set('b', $cacheableobject2)); 2643 $staticaccelerationreturntime = $cache->phpunit_static_acceleration_get('a')->propertytime; 2644 $staticaccelerationreturntimeb = $cache->phpunit_static_acceleration_get('b')->propertytime; 2645 $this->assertGreaterThanOrEqual($startmicrotime, $staticaccelerationreturntime, 'Restore time of static must be newer.'); 2646 2647 // Reset the static cache without resetting backing store. 2648 $cache->phpunit_static_acceleration_purge(); 2649 2650 // Get the value from the backend store, populating the static cache. 2651 $cachevalue = $cache->get('a'); 2652 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $cachevalue); 2653 $this->assertGreaterThanOrEqual($staticaccelerationreturntime, $cachevalue->propertytime); 2654 $backingstorereturntime = $cachevalue->propertytime; 2655 2656 $results = $cache->get_many(array('b')); 2657 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $results['b']); 2658 $this->assertGreaterThanOrEqual($staticaccelerationreturntimeb, $results['b']->propertytime); 2659 $backingstorereturntimeb = $results['b']->propertytime; 2660 2661 // Obtain the value again and confirm that static cache is using wake_from_cache. 2662 // Upon failure, the times are not adjusted as wake_from_cache is skipped as the 2663 // value is stored serialized in the static acceleration cache. 2664 $cachevalue = $cache->phpunit_static_acceleration_get('a'); 2665 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $cachevalue); 2666 $this->assertGreaterThanOrEqual($backingstorereturntime, $cachevalue->propertytime); 2667 2668 $results = $cache->get_many(array('b')); 2669 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $results['b']); 2670 $this->assertGreaterThanOrEqual($backingstorereturntimeb, $results['b']->propertytime); 2671 2672 /** @var cache_phpunit_application $cache */ 2673 $cache = cache::make('phpunit', 'accelerated2'); 2674 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 2675 2676 // Check that the array holds the last accessed items by get/set. 2677 $this->assertTrue($cache->set('a', 'A')); 2678 $this->assertTrue($cache->set('b', 'B')); 2679 $this->assertTrue($cache->set('c', 'C')); 2680 $this->assertTrue($cache->set('d', 'D')); 2681 $this->assertTrue($cache->set('e', 'E')); 2682 // Current keys in the array: c, d, e. 2683 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2684 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2685 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2686 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2687 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2688 2689 $this->assertEquals('A', $cache->get('a')); 2690 // Current keys in the array: d, e, a. 2691 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2692 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2693 $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); 2694 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2695 $this->assertFalse($cache->phpunit_static_acceleration_get('c')); 2696 2697 // Current keys in the array: d, e, a. 2698 $this->assertEquals(array('c' => 'C'), $cache->get_many(array('c'))); 2699 // Current keys in the array: e, a, c. 2700 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2701 $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); 2702 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2703 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2704 $this->assertFalse($cache->phpunit_static_acceleration_get('d')); 2705 2706 2707 $cache = cache::make('phpunit', 'accelerated3'); 2708 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 2709 2710 // Check that the array holds the last accessed items by get/set. 2711 $this->assertTrue($cache->set('a', 'A')); 2712 $this->assertTrue($cache->set('b', 'B')); 2713 $this->assertTrue($cache->set('c', 'C')); 2714 $this->assertTrue($cache->set('d', 'D')); 2715 $this->assertTrue($cache->set('e', 'E')); 2716 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2717 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2718 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2719 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2720 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2721 2722 $this->assertTrue($cache->set('b', 'B2')); 2723 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2724 $this->assertEquals('B2', $cache->phpunit_static_acceleration_get('b')); 2725 $this->assertFalse($cache->phpunit_static_acceleration_get('c')); 2726 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2727 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2728 2729 $this->assertEquals(2, $cache->set_many(array('b' => 'B3', 'c' => 'C3'))); 2730 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2731 $this->assertEquals('B3', $cache->phpunit_static_acceleration_get('b')); 2732 $this->assertEquals('C3', $cache->phpunit_static_acceleration_get('c')); 2733 $this->assertFalse($cache->phpunit_static_acceleration_get('d')); 2734 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2735 2736 $cache = cache::make('phpunit', 'accelerated4'); 2737 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 2738 $this->assertTrue($cache->set('a', 'A')); 2739 $this->assertTrue($cache->set('a', 'A')); 2740 $this->assertTrue($cache->set('a', 'A')); 2741 $this->assertTrue($cache->set('a', 'A')); 2742 $this->assertTrue($cache->set('a', 'A')); 2743 $this->assertTrue($cache->set('a', 'A')); 2744 $this->assertTrue($cache->set('a', 'A')); 2745 $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); 2746 $this->assertEquals('A', $cache->get('a')); 2747 2748 // Setting simpledata to false objects are cloned when retrieving data. 2749 $cache = cache::make('phpunit', 'simpledataarea1'); 2750 $notreallysimple = new \stdClass(); 2751 $notreallysimple->name = 'a'; 2752 $cache->set('a', $notreallysimple); 2753 $returnedinstance1 = $cache->get('a'); 2754 $returnedinstance2 = $cache->get('a'); 2755 $returnedinstance1->name = 'b'; 2756 $this->assertEquals('a', $returnedinstance2->name); 2757 2758 // Setting simpledata to true we assume that data does not contain references. 2759 $cache = cache::make('phpunit', 'simpledataarea2'); 2760 $notreallysimple = new \stdClass(); 2761 $notreallysimple->name = 'a'; 2762 $cache->set('a', $notreallysimple); 2763 $returnedinstance1 = $cache->get('a'); 2764 $returnedinstance2 = $cache->get('a'); 2765 $returnedinstance1->name = 'b'; 2766 $this->assertEquals('b', $returnedinstance2->name); 2767 } 2768 2769 public function test_identifiers_have_separate_caches() { 2770 $cachepg = cache::make('core', 'databasemeta', array('dbfamily' => 'pgsql')); 2771 $cachepg->set(1, 'here'); 2772 $cachemy = cache::make('core', 'databasemeta', array('dbfamily' => 'mysql')); 2773 $cachemy->set(2, 'there'); 2774 $this->assertEquals('here', $cachepg->get(1)); 2775 $this->assertEquals('there', $cachemy->get(2)); 2776 $this->assertFalse($cachemy->get(1)); 2777 } 2778 2779 public function test_performance_debug() { 2780 global $CFG; 2781 $this->resetAfterTest(true); 2782 $CFG->perfdebug = 15; 2783 2784 $instance = cache_config_testing::instance(); 2785 $applicationid = 'phpunit/applicationperf'; 2786 $instance->phpunit_add_definition($applicationid, array( 2787 'mode' => cache_store::MODE_APPLICATION, 2788 'component' => 'phpunit', 2789 'area' => 'applicationperf' 2790 )); 2791 $sessionid = 'phpunit/sessionperf'; 2792 $instance->phpunit_add_definition($sessionid, array( 2793 'mode' => cache_store::MODE_SESSION, 2794 'component' => 'phpunit', 2795 'area' => 'sessionperf' 2796 )); 2797 $requestid = 'phpunit/requestperf'; 2798 $instance->phpunit_add_definition($requestid, array( 2799 'mode' => cache_store::MODE_REQUEST, 2800 'component' => 'phpunit', 2801 'area' => 'requestperf' 2802 )); 2803 2804 $application = cache::make('phpunit', 'applicationperf'); 2805 $session = cache::make('phpunit', 'sessionperf'); 2806 $request = cache::make('phpunit', 'requestperf'); 2807 2808 // Check that no stats are recorded for these definitions yet. 2809 $stats = cache_helper::get_stats(); 2810 $this->assertArrayNotHasKey($applicationid, $stats); 2811 $this->assertArrayHasKey($sessionid, $stats); // Session cache sets a key on construct. 2812 $this->assertArrayNotHasKey($requestid, $stats); 2813 2814 // Check that stores register misses. 2815 $this->assertFalse($application->get('missMe')); 2816 $this->assertFalse($application->get('missMe')); 2817 $this->assertFalse($session->get('missMe')); 2818 $this->assertFalse($session->get('missMe')); 2819 $this->assertFalse($session->get('missMe')); 2820 $this->assertFalse($request->get('missMe')); 2821 $this->assertFalse($request->get('missMe')); 2822 $this->assertFalse($request->get('missMe')); 2823 $this->assertFalse($request->get('missMe')); 2824 2825 $endstats = cache_helper::get_stats(); 2826 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['misses']); 2827 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits']); 2828 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets']); 2829 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['misses']); 2830 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits']); 2831 $this->assertEquals(1, $endstats[$sessionid]['stores']['default_session']['sets']); 2832 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['misses']); 2833 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits']); 2834 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets']); 2835 2836 $startstats = cache_helper::get_stats(); 2837 2838 // Check that stores register sets. 2839 $this->assertTrue($application->set('setMe1', 1)); 2840 $this->assertTrue($application->set('setMe2', 2)); 2841 $this->assertTrue($session->set('setMe1', 1)); 2842 $this->assertTrue($session->set('setMe2', 2)); 2843 $this->assertTrue($session->set('setMe3', 3)); 2844 $this->assertTrue($request->set('setMe1', 1)); 2845 $this->assertTrue($request->set('setMe2', 2)); 2846 $this->assertTrue($request->set('setMe3', 3)); 2847 $this->assertTrue($request->set('setMe4', 4)); 2848 2849 $endstats = cache_helper::get_stats(); 2850 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - 2851 $startstats[$applicationid]['stores']['default_application']['misses']); 2852 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] - 2853 $startstats[$applicationid]['stores']['default_application']['hits']); 2854 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] - 2855 $startstats[$applicationid]['stores']['default_application']['sets']); 2856 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - 2857 $startstats[$sessionid]['stores']['default_session']['misses']); 2858 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] - 2859 $startstats[$sessionid]['stores']['default_session']['hits']); 2860 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] - 2861 $startstats[$sessionid]['stores']['default_session']['sets']); 2862 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - 2863 $startstats[$requestid]['stores']['default_request']['misses']); 2864 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] - 2865 $startstats[$requestid]['stores']['default_request']['hits']); 2866 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] - 2867 $startstats[$requestid]['stores']['default_request']['sets']); 2868 2869 $startstats = cache_helper::get_stats(); 2870 2871 // Check that stores register hits. 2872 $this->assertEquals($application->get('setMe1'), 1); 2873 $this->assertEquals($application->get('setMe2'), 2); 2874 $this->assertEquals($session->get('setMe1'), 1); 2875 $this->assertEquals($session->get('setMe2'), 2); 2876 $this->assertEquals($session->get('setMe3'), 3); 2877 $this->assertEquals($request->get('setMe1'), 1); 2878 $this->assertEquals($request->get('setMe2'), 2); 2879 $this->assertEquals($request->get('setMe3'), 3); 2880 $this->assertEquals($request->get('setMe4'), 4); 2881 2882 $endstats = cache_helper::get_stats(); 2883 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - 2884 $startstats[$applicationid]['stores']['default_application']['misses']); 2885 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] - 2886 $startstats[$applicationid]['stores']['default_application']['hits']); 2887 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] - 2888 $startstats[$applicationid]['stores']['default_application']['sets']); 2889 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - 2890 $startstats[$sessionid]['stores']['default_session']['misses']); 2891 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] - 2892 $startstats[$sessionid]['stores']['default_session']['hits']); 2893 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] - 2894 $startstats[$sessionid]['stores']['default_session']['sets']); 2895 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - 2896 $startstats[$requestid]['stores']['default_request']['misses']); 2897 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] - 2898 $startstats[$requestid]['stores']['default_request']['hits']); 2899 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] - 2900 $startstats[$requestid]['stores']['default_request']['sets']); 2901 2902 $startstats = cache_helper::get_stats(); 2903 2904 // Check that stores register through get_many. 2905 $application->get_many(array('setMe1', 'setMe2')); 2906 $session->get_many(array('setMe1', 'setMe2', 'setMe3')); 2907 $request->get_many(array('setMe1', 'setMe2', 'setMe3', 'setMe4')); 2908 2909 $endstats = cache_helper::get_stats(); 2910 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - 2911 $startstats[$applicationid]['stores']['default_application']['misses']); 2912 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] - 2913 $startstats[$applicationid]['stores']['default_application']['hits']); 2914 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] - 2915 $startstats[$applicationid]['stores']['default_application']['sets']); 2916 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - 2917 $startstats[$sessionid]['stores']['default_session']['misses']); 2918 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] - 2919 $startstats[$sessionid]['stores']['default_session']['hits']); 2920 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] - 2921 $startstats[$sessionid]['stores']['default_session']['sets']); 2922 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - 2923 $startstats[$requestid]['stores']['default_request']['misses']); 2924 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] - 2925 $startstats[$requestid]['stores']['default_request']['hits']); 2926 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] - 2927 $startstats[$requestid]['stores']['default_request']['sets']); 2928 2929 $startstats = cache_helper::get_stats(); 2930 2931 // Check that stores register through set_many. 2932 $this->assertEquals(2, $application->set_many(['setMe1' => 1, 'setMe2' => 2])); 2933 $this->assertEquals(3, $session->set_many(['setMe1' => 1, 'setMe2' => 2, 'setMe3' => 3])); 2934 $this->assertEquals(4, $request->set_many(['setMe1' => 1, 'setMe2' => 2, 'setMe3' => 3, 'setMe4' => 4])); 2935 2936 $endstats = cache_helper::get_stats(); 2937 2938 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - 2939 $startstats[$applicationid]['stores']['default_application']['misses']); 2940 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] - 2941 $startstats[$applicationid]['stores']['default_application']['hits']); 2942 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] - 2943 $startstats[$applicationid]['stores']['default_application']['sets']); 2944 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - 2945 $startstats[$sessionid]['stores']['default_session']['misses']); 2946 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] - 2947 $startstats[$sessionid]['stores']['default_session']['hits']); 2948 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] - 2949 $startstats[$sessionid]['stores']['default_session']['sets']); 2950 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - 2951 $startstats[$requestid]['stores']['default_request']['misses']); 2952 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] - 2953 $startstats[$requestid]['stores']['default_request']['hits']); 2954 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] - 2955 $startstats[$requestid]['stores']['default_request']['sets']); 2956 } 2957 2958 /** 2959 * Data provider for static acceleration performance tests. 2960 * 2961 * @return array 2962 */ 2963 public function static_acceleration_performance_provider(): array { 2964 // Note: These are the delta values, not the absolute values. 2965 // Also note that the set will actually store the valuein the static cache immediately. 2966 $validfirst = [ 2967 'default_application' => [ 2968 'hits' => 1, 2969 'misses' => 0, 2970 ], 2971 cache_store::STATIC_ACCEL => [ 2972 'hits' => 0, 2973 'misses' => 1, 2974 ], 2975 ]; 2976 2977 $validsecond = [ 2978 'default_application' => [ 2979 'hits' => 0, 2980 'misses' => 0, 2981 ], 2982 cache_store::STATIC_ACCEL => [ 2983 'hits' => 1, 2984 'misses' => 0, 2985 ], 2986 ]; 2987 2988 $invalidfirst = [ 2989 'default_application' => [ 2990 'hits' => 0, 2991 'misses' => 1, 2992 ], 2993 cache_store::STATIC_ACCEL => [ 2994 'hits' => 0, 2995 'misses' => 1, 2996 ], 2997 ]; 2998 $invalidsecond = [ 2999 'default_application' => [ 3000 'hits' => 0, 3001 'misses' => 1, 3002 ], 3003 cache_store::STATIC_ACCEL => [ 3004 'hits' => 0, 3005 'misses' => 1, 3006 ], 3007 ];; 3008 3009 return [ 3010 'Truthy' => [ 3011 true, 3012 $validfirst, 3013 $validsecond, 3014 ], 3015 'Null' => [ 3016 null, 3017 $validfirst, 3018 $validsecond, 3019 ], 3020 'Empty Array' => [ 3021 [], 3022 $validfirst, 3023 $validsecond, 3024 ], 3025 'Empty String' => [ 3026 '', 3027 $validfirst, 3028 $validsecond, 3029 ], 3030 'False' => [ 3031 false, 3032 $invalidfirst, 3033 $invalidsecond, 3034 ], 3035 ]; 3036 } 3037 3038 /** 3039 * Test performance of static acceleration caches with values which are frequently confused with missing values. 3040 * 3041 * @dataProvider static_acceleration_performance_provider 3042 * @param mixed $value The value to test 3043 * @param array $firstfetchstats The expected stats on the first fetch 3044 * @param array $secondfetchstats The expected stats on the subsequent fetch 3045 */ 3046 public function test_static_acceleration_values_performance( 3047 $value, 3048 array $firstfetchstats, 3049 array $secondfetchstats, 3050 ): void { 3051 // Note: We need to modify perfdebug to test this. 3052 global $CFG; 3053 $this->resetAfterTest(true); 3054 $CFG->perfdebug = 15; 3055 3056 $instance = cache_config_testing::instance(); 3057 $instance->phpunit_add_definition('phpunit/accelerated', [ 3058 'mode' => cache_store::MODE_APPLICATION, 3059 'component' => 'phpunit', 3060 'area' => 'accelerated', 3061 'staticacceleration' => true, 3062 'staticaccelerationsize' => 1, 3063 ]); 3064 3065 $cache = cache::make('phpunit', 'accelerated'); 3066 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 3067 3068 $this->assertTrue($cache->set('value', $value)); 3069 3070 $checkstats = function( 3071 array $start, 3072 array $expectedstats, 3073 ): array { 3074 $applicationid = 'phpunit/accelerated'; 3075 $endstats = cache_helper::get_stats(); 3076 3077 $start = $start[$applicationid]['stores']; 3078 $end = $endstats[$applicationid]['stores']; 3079 3080 foreach ($expectedstats as $cachename => $expected) { 3081 foreach ($expected as $type => $value) { 3082 $startvalue = array_key_exists($cachename, $start) ? $start[$cachename][$type] : 0; 3083 $endvalue = array_key_exists($cachename, $end) ? $end[$cachename][$type] : 0; 3084 $diff = $endvalue - $startvalue; 3085 $this->assertEquals( 3086 $value, 3087 $diff, 3088 "Expected $cachename $type to be $value, got $diff", 3089 ); 3090 } 3091 } 3092 3093 return $endstats; 3094 }; 3095 3096 // Reset the cache factory so that we can get the stats from a fresh instance. 3097 $factory = cache_factory::instance(); 3098 $factory->reset_cache_instances(); 3099 $cache = cache::make('phpunit', 'accelerated'); 3100 3101 // Get the initial stats. 3102 $startstats = cache_helper::get_stats(); 3103 3104 // Fetching the value the first time should seed the static cache from the application cache. 3105 $this->assertEquals($value, $cache->get('value')); 3106 $startstats = $checkstats($startstats, $firstfetchstats); 3107 3108 // Fetching the value should only hit the static cache. 3109 $this->assertEquals($value, $cache->get('value')); 3110 $checkstats($startstats, $secondfetchstats); 3111 } 3112 3113 3114 public function test_static_cache() { 3115 global $CFG; 3116 $this->resetAfterTest(true); 3117 $CFG->perfdebug = 15; 3118 3119 // Create cache store with static acceleration. 3120 $instance = cache_config_testing::instance(); 3121 $applicationid = 'phpunit/applicationperf'; 3122 $instance->phpunit_add_definition($applicationid, array( 3123 'mode' => cache_store::MODE_APPLICATION, 3124 'component' => 'phpunit', 3125 'area' => 'applicationperf', 3126 'simplekeys' => true, 3127 'staticacceleration' => true, 3128 'staticaccelerationsize' => 3 3129 )); 3130 3131 $application = cache::make('phpunit', 'applicationperf'); 3132 3133 // Check that stores register sets. 3134 $this->assertTrue($application->set('setMe1', 1)); 3135 $this->assertTrue($application->set('setMe2', 0)); 3136 $this->assertTrue($application->set('setMe3', array())); 3137 $this->assertTrue($application->get('setMe1') !== false); 3138 $this->assertTrue($application->get('setMe2') !== false); 3139 $this->assertTrue($application->get('setMe3') !== false); 3140 3141 // Check that the static acceleration worked, even on empty arrays and the number 0. 3142 $endstats = cache_helper::get_stats(); 3143 $this->assertEquals(0, $endstats[$applicationid]['stores']['** static accel. **']['misses']); 3144 $this->assertEquals(3, $endstats[$applicationid]['stores']['** static accel. **']['hits']); 3145 } 3146 3147 public function test_performance_debug_off() { 3148 global $CFG; 3149 $this->resetAfterTest(true); 3150 $CFG->perfdebug = 7; 3151 3152 $instance = cache_config_testing::instance(); 3153 $applicationid = 'phpunit/applicationperfoff'; 3154 $instance->phpunit_add_definition($applicationid, array( 3155 'mode' => cache_store::MODE_APPLICATION, 3156 'component' => 'phpunit', 3157 'area' => 'applicationperfoff' 3158 )); 3159 $sessionid = 'phpunit/sessionperfoff'; 3160 $instance->phpunit_add_definition($sessionid, array( 3161 'mode' => cache_store::MODE_SESSION, 3162 'component' => 'phpunit', 3163 'area' => 'sessionperfoff' 3164 )); 3165 $requestid = 'phpunit/requestperfoff'; 3166 $instance->phpunit_add_definition($requestid, array( 3167 'mode' => cache_store::MODE_REQUEST, 3168 'component' => 'phpunit', 3169 'area' => 'requestperfoff' 3170 )); 3171 3172 $application = cache::make('phpunit', 'applicationperfoff'); 3173 $session = cache::make('phpunit', 'sessionperfoff'); 3174 $request = cache::make('phpunit', 'requestperfoff'); 3175 3176 // Check that no stats are recorded for these definitions yet. 3177 $stats = cache_helper::get_stats(); 3178 $this->assertArrayNotHasKey($applicationid, $stats); 3179 $this->assertArrayNotHasKey($sessionid, $stats); 3180 $this->assertArrayNotHasKey($requestid, $stats); 3181 3182 // Trigger cache misses, cache sets and cache hits. 3183 $this->assertFalse($application->get('missMe')); 3184 $this->assertTrue($application->set('setMe', 1)); 3185 $this->assertEquals(1, $application->get('setMe')); 3186 $this->assertFalse($session->get('missMe')); 3187 $this->assertTrue($session->set('setMe', 3)); 3188 $this->assertEquals(3, $session->get('setMe')); 3189 $this->assertFalse($request->get('missMe')); 3190 $this->assertTrue($request->set('setMe', 4)); 3191 $this->assertEquals(4, $request->get('setMe')); 3192 3193 // Check that no stats are being recorded for these definitions. 3194 $endstats = cache_helper::get_stats(); 3195 $this->assertArrayNotHasKey($applicationid, $endstats); 3196 $this->assertArrayNotHasKey($sessionid, $endstats); 3197 $this->assertArrayNotHasKey($requestid, $endstats); 3198 } 3199 3200 /** 3201 * Tests session cache event purge and subsequent visit in the same request. 3202 * 3203 * This test simulates a cache being created, a value being set, then the value being purged. 3204 * A subsequent use of the same cache is started in the same request which fills the cache. 3205 * A new request is started a short time later. 3206 * The cache should be filled. 3207 */ 3208 public function test_session_event_purge_same_second() { 3209 $instance = cache_config_testing::instance(); 3210 $instance->phpunit_add_definition('phpunit/eventpurgetest', array( 3211 'mode' => cache_store::MODE_SESSION, 3212 'component' => 'phpunit', 3213 'area' => 'eventpurgetest', 3214 'invalidationevents' => array( 3215 'crazyevent', 3216 ) 3217 )); 3218 3219 // Create the cache, set a value, and immediately purge it by event. 3220 $cache = cache::make('phpunit', 'eventpurgetest'); 3221 $cache->set('testkey1', 'test data 1'); 3222 $this->assertEquals('test data 1', $cache->get('testkey1')); 3223 cache_helper::purge_by_event('crazyevent'); 3224 $this->assertFalse($cache->get('testkey1')); 3225 3226 // Set up the cache again in the same request and add a new value back in. 3227 $factory = cache_factory::instance(); 3228 $factory->reset_cache_instances(); 3229 $cache = cache::make('phpunit', 'eventpurgetest'); 3230 $cache->set('testkey1', 'test data 2'); 3231 $this->assertEquals('test data 2', $cache->get('testkey1')); 3232 3233 // Trick the cache into thinking that this is a new request. 3234 cache_phpunit_cache::simulate_new_request(); 3235 $factory = cache_factory::instance(); 3236 $factory->reset_cache_instances(); 3237 3238 // Set up the cache again. 3239 // This is a subsequent request at a new time, so we instead the invalidation time will be checked. 3240 // The invalidation time should match the last purged time and the cache will not be re-purged. 3241 $cache = cache::make('phpunit', 'eventpurgetest'); 3242 $this->assertEquals('test data 2', $cache->get('testkey1')); 3243 } 3244 3245 /** 3246 * Test that values set in different sessions are stored with different key prefixes. 3247 */ 3248 public function test_session_distinct_storage_key() { 3249 $this->resetAfterTest(); 3250 3251 // Prepare a dummy session cache configuration. 3252 $config = cache_config_testing::instance(); 3253 $config->phpunit_add_definition('phpunit/test_session_distinct_storage_key', array( 3254 'mode' => cache_store::MODE_SESSION, 3255 'component' => 'phpunit', 3256 'area' => 'test_session_distinct_storage_key' 3257 )); 3258 3259 // First anonymous user's session cache. 3260 cache_phpunit_session::phpunit_mockup_session_id('foo'); 3261 $this->setUser(0); 3262 $cache1 = cache::make('phpunit', 'test_session_distinct_storage_key'); 3263 3264 // Reset cache instances to emulate a new request. 3265 cache_factory::instance()->reset_cache_instances(); 3266 3267 // Another anonymous user's session cache. 3268 cache_phpunit_session::phpunit_mockup_session_id('bar'); 3269 $this->setUser(0); 3270 $cache2 = cache::make('phpunit', 'test_session_distinct_storage_key'); 3271 3272 cache_factory::instance()->reset_cache_instances(); 3273 3274 // Guest user's session cache. 3275 cache_phpunit_session::phpunit_mockup_session_id('baz'); 3276 $this->setGuestUser(); 3277 $cache3 = cache::make('phpunit', 'test_session_distinct_storage_key'); 3278 3279 cache_factory::instance()->reset_cache_instances(); 3280 3281 // Same guest user's session cache but in another browser window. 3282 cache_phpunit_session::phpunit_mockup_session_id('baz'); 3283 $this->setGuestUser(); 3284 $cache4 = cache::make('phpunit', 'test_session_distinct_storage_key'); 3285 3286 // Assert that different PHP session implies different key prefix for storing values. 3287 $this->assertNotEquals($cache1->phpunit_get_key_prefix(), $cache2->phpunit_get_key_prefix()); 3288 3289 // Assert that same PHP session implies same key prefix for storing values. 3290 $this->assertEquals($cache3->phpunit_get_key_prefix(), $cache4->phpunit_get_key_prefix()); 3291 } 3292 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body