See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401] [Versions 401 and 402] [Versions 401 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 $cache1 = cache::make('phpunit', 'lockingtest'); 887 $cache2 = clone($cache1); 888 889 $this->assertTrue($cache1->set('testkey', 'test data')); 890 $this->assertTrue($cache2->set('testkey', 'test data')); 891 892 $this->assertTrue($cache1->acquire_lock('testkey')); 893 $this->assertFalse($cache2->acquire_lock('testkey')); 894 895 $this->assertTrue($cache1->check_lock_state('testkey')); 896 $this->assertFalse($cache2->check_lock_state('testkey')); 897 898 $this->assertTrue($cache1->release_lock('testkey')); 899 $this->assertFalse($cache2->release_lock('testkey')); 900 901 $this->assertTrue($cache1->set('testkey', 'test data')); 902 $this->assertTrue($cache2->set('testkey', 'test data')); 903 } 904 905 /** 906 * Tests application cache event invalidation 907 */ 908 public function test_application_event_invalidation() { 909 $instance = cache_config_testing::instance(); 910 $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 911 'mode' => cache_store::MODE_APPLICATION, 912 'component' => 'phpunit', 913 'area' => 'eventinvalidationtest', 914 'invalidationevents' => array( 915 'crazyevent' 916 ) 917 )); 918 $cache = cache::make('phpunit', 'eventinvalidationtest'); 919 920 $this->assertTrue($cache->set('testkey1', 'test data 1')); 921 $this->assertEquals('test data 1', $cache->get('testkey1')); 922 $this->assertTrue($cache->set('testkey2', 'test data 2')); 923 $this->assertEquals('test data 2', $cache->get('testkey2')); 924 925 // Test invalidating a single entry. 926 cache_helper::invalidate_by_event('crazyevent', array('testkey1')); 927 928 $this->assertFalse($cache->get('testkey1')); 929 $this->assertEquals('test data 2', $cache->get('testkey2')); 930 931 $this->assertTrue($cache->set('testkey1', 'test data 1')); 932 933 // Test invalidating both entries. 934 cache_helper::invalidate_by_event('crazyevent', array('testkey1', 'testkey2')); 935 936 $this->assertFalse($cache->get('testkey1')); 937 $this->assertFalse($cache->get('testkey2')); 938 } 939 940 /** 941 * Tests session cache event invalidation 942 */ 943 public function test_session_event_invalidation() { 944 $instance = cache_config_testing::instance(); 945 $instance->phpunit_add_definition('phpunit/test_session_event_invalidation', array( 946 'mode' => cache_store::MODE_SESSION, 947 'component' => 'phpunit', 948 'area' => 'test_session_event_invalidation', 949 'invalidationevents' => array( 950 'crazyevent' 951 ) 952 )); 953 $cache = cache::make('phpunit', 'test_session_event_invalidation'); 954 $this->assertInstanceOf(cache_session::class, $cache); 955 956 $this->assertTrue($cache->set('testkey1', 'test data 1')); 957 $this->assertEquals('test data 1', $cache->get('testkey1')); 958 $this->assertTrue($cache->set('testkey2', 'test data 2')); 959 $this->assertEquals('test data 2', $cache->get('testkey2')); 960 961 // Test invalidating a single entry. 962 cache_helper::invalidate_by_event('crazyevent', array('testkey1')); 963 964 $this->assertFalse($cache->get('testkey1')); 965 $this->assertEquals('test data 2', $cache->get('testkey2')); 966 967 $this->assertTrue($cache->set('testkey1', 'test data 1')); 968 969 // Test invalidating both entries. 970 cache_helper::invalidate_by_event('crazyevent', array('testkey1', 'testkey2')); 971 972 $this->assertFalse($cache->get('testkey1')); 973 $this->assertFalse($cache->get('testkey2')); 974 } 975 976 /** 977 * Tests application cache definition invalidation 978 */ 979 public function test_application_definition_invalidation() { 980 $instance = cache_config_testing::instance(); 981 $instance->phpunit_add_definition('phpunit/definitioninvalidation', array( 982 'mode' => cache_store::MODE_APPLICATION, 983 'component' => 'phpunit', 984 'area' => 'definitioninvalidation' 985 )); 986 $cache = cache::make('phpunit', 'definitioninvalidation'); 987 $this->assertTrue($cache->set('testkey1', 'test data 1')); 988 $this->assertEquals('test data 1', $cache->get('testkey1')); 989 $this->assertTrue($cache->set('testkey2', 'test data 2')); 990 $this->assertEquals('test data 2', $cache->get('testkey2')); 991 992 cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), 'testkey1'); 993 994 $this->assertFalse($cache->get('testkey1')); 995 $this->assertEquals('test data 2', $cache->get('testkey2')); 996 997 $this->assertTrue($cache->set('testkey1', 'test data 1')); 998 999 cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1')); 1000 1001 $this->assertFalse($cache->get('testkey1')); 1002 $this->assertEquals('test data 2', $cache->get('testkey2')); 1003 1004 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1005 1006 cache_helper::invalidate_by_definition('phpunit', 'definitioninvalidation', array(), array('testkey1', 'testkey2')); 1007 1008 $this->assertFalse($cache->get('testkey1')); 1009 $this->assertFalse($cache->get('testkey2')); 1010 } 1011 1012 /** 1013 * Tests session cache definition invalidation 1014 */ 1015 public function test_session_definition_invalidation() { 1016 $instance = cache_config_testing::instance(); 1017 $instance->phpunit_add_definition('phpunit/test_session_definition_invalidation', array( 1018 'mode' => cache_store::MODE_SESSION, 1019 'component' => 'phpunit', 1020 'area' => 'test_session_definition_invalidation' 1021 )); 1022 $cache = cache::make('phpunit', 'test_session_definition_invalidation'); 1023 $this->assertInstanceOf(cache_session::class, $cache); 1024 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1025 $this->assertEquals('test data 1', $cache->get('testkey1')); 1026 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1027 $this->assertEquals('test data 2', $cache->get('testkey2')); 1028 1029 cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), 'testkey1'); 1030 1031 $this->assertFalse($cache->get('testkey1')); 1032 $this->assertEquals('test data 2', $cache->get('testkey2')); 1033 1034 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1035 1036 cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), 1037 array('testkey1')); 1038 1039 $this->assertFalse($cache->get('testkey1')); 1040 $this->assertEquals('test data 2', $cache->get('testkey2')); 1041 1042 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1043 1044 cache_helper::invalidate_by_definition('phpunit', 'test_session_definition_invalidation', array(), 1045 array('testkey1', 'testkey2')); 1046 1047 $this->assertFalse($cache->get('testkey1')); 1048 $this->assertFalse($cache->get('testkey2')); 1049 } 1050 1051 /** 1052 * Tests application cache event invalidation over a distributed setup. 1053 */ 1054 public function test_distributed_application_event_invalidation() { 1055 global $CFG; 1056 // This is going to be an intense wee test. 1057 // We need to add data the to cache, invalidate it by event, manually force it back without MUC knowing to simulate a 1058 // disconnected/distributed setup (think load balanced server using local cache), instantiate the cache again and finally 1059 // check that it is not picked up. 1060 $instance = cache_config_testing::instance(); 1061 $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 1062 'mode' => cache_store::MODE_APPLICATION, 1063 'component' => 'phpunit', 1064 'area' => 'eventinvalidationtest', 1065 'simplekeys' => true, 1066 'simpledata' => true, 1067 'invalidationevents' => array( 1068 'crazyevent' 1069 ) 1070 )); 1071 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1072 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1073 $this->assertEquals('test data 1', $cache->get('testkey1')); 1074 1075 cache_helper::invalidate_by_event('crazyevent', array('testkey1')); 1076 1077 $this->assertFalse($cache->get('testkey1')); 1078 1079 // OK data added, data invalidated, and invalidation time has been set. 1080 // Now we need to manually add back the data and adjust the invalidation time. 1081 $hash = md5(cache_store::MODE_APPLICATION.'/phpunit/eventinvalidationtest/'.$CFG->wwwroot.'phpunit'); 1082 $timefile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/las-cache/lastinvalidation-$hash.cache"; 1083 // Make sure the file is correct. 1084 $this->assertTrue(file_exists($timefile)); 1085 $timecont = serialize(cache::now(true) - 60); // Back 60sec in the past to force it to re-invalidate. 1086 make_writable_directory(dirname($timefile)); 1087 file_put_contents($timefile, $timecont); 1088 $this->assertTrue(file_exists($timefile)); 1089 1090 $datafile = $CFG->dataroot."/cache/cachestore_file/default_application/phpunit_eventinvalidationtest/tes-cache/testkey1-$hash.cache"; 1091 $datacont = serialize("test data 1"); 1092 make_writable_directory(dirname($datafile)); 1093 file_put_contents($datafile, $datacont); 1094 $this->assertTrue(file_exists($datafile)); 1095 1096 // Test 1: Rebuild without the event and test its there. 1097 cache_factory::reset(); 1098 $instance = cache_config_testing::instance(); 1099 $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 1100 'mode' => cache_store::MODE_APPLICATION, 1101 'component' => 'phpunit', 1102 'area' => 'eventinvalidationtest', 1103 'simplekeys' => true, 1104 'simpledata' => true, 1105 )); 1106 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1107 $this->assertEquals('test data 1', $cache->get('testkey1')); 1108 1109 // Test 2: Rebuild and test the invalidation of the event via the invalidation cache. 1110 cache_factory::reset(); 1111 1112 $instance = cache_config_testing::instance(); 1113 $instance->phpunit_add_definition('phpunit/eventinvalidationtest', array( 1114 'mode' => cache_store::MODE_APPLICATION, 1115 'component' => 'phpunit', 1116 'area' => 'eventinvalidationtest', 1117 'simplekeys' => true, 1118 'simpledata' => true, 1119 'invalidationevents' => array( 1120 'crazyevent' 1121 ) 1122 )); 1123 1124 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1125 $this->assertFalse($cache->get('testkey1')); 1126 1127 // Test 3: Verify that an existing lastinvalidation cache file is updated when needed. 1128 1129 // Make a new cache class. This should should invalidate testkey2. 1130 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1131 1132 // Invalidation token should have been reset. 1133 $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); 1134 1135 // Set testkey2 data. 1136 $cache->set('testkey2', 'test data 2'); 1137 1138 // Backdate the event invalidation time by 30 seconds. 1139 $invalidationcache = cache::make('core', 'eventinvalidation'); 1140 $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 30)); 1141 1142 // Lastinvalidation should already be cache::now(). 1143 $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); 1144 1145 // Set it to 15 seconds ago so that we know if it changes. 1146 $pasttime = cache::now(true) - 15; 1147 $cache->set('lastinvalidation', $pasttime); 1148 1149 // Make a new cache class. This should not invalidate anything. 1150 cache_factory::instance()->reset_cache_instances(); 1151 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1152 1153 // Lastinvalidation shouldn't change since it was already newer than invalidation event. 1154 $this->assertEquals($pasttime, $cache->get('lastinvalidation')); 1155 1156 // Now set the event invalidation to newer than the lastinvalidation time. 1157 $invalidationcache->set('crazyevent', array('testkey2' => cache::now() - 5)); 1158 // Make a new cache class. This should should invalidate testkey2. 1159 cache_factory::instance()->reset_cache_instances(); 1160 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1161 // Lastinvalidation timestamp should have updated to cache::now(). 1162 $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); 1163 1164 // Now simulate a purge_by_event 5 seconds ago. 1165 $invalidationcache = cache::make('core', 'eventinvalidation'); 1166 $invalidationcache->set('crazyevent', array('purged' => cache::now(true) - 5)); 1167 // Set our lastinvalidation timestamp to 15 seconds ago. 1168 $cache->set('lastinvalidation', cache::now(true) - 15); 1169 // Make a new cache class. This should invalidate the cache. 1170 cache_factory::instance()->reset_cache_instances(); 1171 $cache = cache::make('phpunit', 'eventinvalidationtest'); 1172 // Lastinvalidation timestamp should have updated to cache::now(). 1173 $this->assertEquals(cache::get_purge_token(), $cache->get('lastinvalidation')); 1174 1175 } 1176 1177 /** 1178 * Tests application cache event purge 1179 */ 1180 public function test_application_event_purge() { 1181 $instance = cache_config_testing::instance(); 1182 $instance->phpunit_add_definition('phpunit/eventpurgetest', array( 1183 'mode' => cache_store::MODE_APPLICATION, 1184 'component' => 'phpunit', 1185 'area' => 'eventpurgetest', 1186 'invalidationevents' => array( 1187 'crazyevent' 1188 ) 1189 )); 1190 $instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array( 1191 'mode' => cache_store::MODE_APPLICATION, 1192 'component' => 'phpunit', 1193 'area' => 'eventpurgetestaccelerated', 1194 'staticacceleration' => true, 1195 'invalidationevents' => array( 1196 'crazyevent' 1197 ) 1198 )); 1199 $cache = cache::make('phpunit', 'eventpurgetest'); 1200 1201 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1202 $this->assertEquals('test data 1', $cache->get('testkey1')); 1203 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1204 $this->assertEquals('test data 2', $cache->get('testkey2')); 1205 1206 // Purge the event. 1207 cache_helper::purge_by_event('crazyevent'); 1208 1209 // Check things have been removed. 1210 $this->assertFalse($cache->get('testkey1')); 1211 $this->assertFalse($cache->get('testkey2')); 1212 1213 // Now test the static acceleration array. 1214 $cache = cache::make('phpunit', 'eventpurgetestaccelerated'); 1215 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1216 $this->assertEquals('test data 1', $cache->get('testkey1')); 1217 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1218 $this->assertEquals('test data 2', $cache->get('testkey2')); 1219 1220 // Purge the event. 1221 cache_helper::purge_by_event('crazyevent'); 1222 1223 // Check things have been removed. 1224 $this->assertFalse($cache->get('testkey1')); 1225 $this->assertFalse($cache->get('testkey2')); 1226 } 1227 1228 /** 1229 * Tests session cache event purge 1230 */ 1231 public function test_session_event_purge() { 1232 $instance = cache_config_testing::instance(); 1233 $instance->phpunit_add_definition('phpunit/eventpurgetest', array( 1234 'mode' => cache_store::MODE_SESSION, 1235 'component' => 'phpunit', 1236 'area' => 'eventpurgetest', 1237 'invalidationevents' => array( 1238 'crazyevent' 1239 ) 1240 )); 1241 $instance->phpunit_add_definition('phpunit/eventpurgetestaccelerated', array( 1242 'mode' => cache_store::MODE_SESSION, 1243 'component' => 'phpunit', 1244 'area' => 'eventpurgetestaccelerated', 1245 'staticacceleration' => true, 1246 'invalidationevents' => array( 1247 'crazyevent' 1248 ) 1249 )); 1250 $cache = cache::make('phpunit', 'eventpurgetest'); 1251 1252 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1253 $this->assertEquals('test data 1', $cache->get('testkey1')); 1254 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1255 $this->assertEquals('test data 2', $cache->get('testkey2')); 1256 1257 // Purge the event. 1258 cache_helper::purge_by_event('crazyevent'); 1259 1260 // Check things have been removed. 1261 $this->assertFalse($cache->get('testkey1')); 1262 $this->assertFalse($cache->get('testkey2')); 1263 1264 // Now test the static acceleration array. 1265 $cache = cache::make('phpunit', 'eventpurgetestaccelerated'); 1266 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1267 $this->assertEquals('test data 1', $cache->get('testkey1')); 1268 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1269 $this->assertEquals('test data 2', $cache->get('testkey2')); 1270 1271 // Purge the event. 1272 cache_helper::purge_by_event('crazyevent'); 1273 1274 // Check things have been removed. 1275 $this->assertFalse($cache->get('testkey1')); 1276 $this->assertFalse($cache->get('testkey2')); 1277 } 1278 1279 /** 1280 * Tests application cache definition purge 1281 */ 1282 public function test_application_definition_purge() { 1283 $instance = cache_config_testing::instance(); 1284 $instance->phpunit_add_definition('phpunit/definitionpurgetest', array( 1285 'mode' => cache_store::MODE_APPLICATION, 1286 'component' => 'phpunit', 1287 'area' => 'definitionpurgetest', 1288 'invalidationevents' => array( 1289 'crazyevent' 1290 ) 1291 )); 1292 $cache = cache::make('phpunit', 'definitionpurgetest'); 1293 1294 $this->assertTrue($cache->set('testkey1', 'test data 1')); 1295 $this->assertEquals('test data 1', $cache->get('testkey1')); 1296 $this->assertTrue($cache->set('testkey2', 'test data 2')); 1297 $this->assertEquals('test data 2', $cache->get('testkey2')); 1298 1299 // Purge the event. 1300 cache_helper::purge_by_definition('phpunit', 'definitionpurgetest'); 1301 1302 // Check things have been removed. 1303 $this->assertFalse($cache->get('testkey1')); 1304 $this->assertFalse($cache->get('testkey2')); 1305 } 1306 1307 /** 1308 * Test the use of an alt path. 1309 * If we can generate a config instance we are done :) 1310 */ 1311 public function test_alt_cache_path() { 1312 global $CFG; 1313 if ((defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) || !empty($CFG->altcacheconfigpath)) { 1314 $this->markTestSkipped('Skipped testing alt cache path as it is already being used.'); 1315 } 1316 $this->resetAfterTest(); 1317 $CFG->altcacheconfigpath = $CFG->dataroot.'/cache/altcacheconfigpath'; 1318 $instance = cache_config_testing::instance(); 1319 $this->assertInstanceOf(cache_config::class, $instance); 1320 } 1321 1322 /** 1323 * Test disabling the cache stores. 1324 */ 1325 public function test_disable_stores() { 1326 $instance = cache_config_testing::instance(); 1327 $instance->phpunit_add_definition('phpunit/disabletest1', array( 1328 'mode' => cache_store::MODE_APPLICATION, 1329 'component' => 'phpunit', 1330 'area' => 'disabletest1' 1331 )); 1332 $instance->phpunit_add_definition('phpunit/disabletest2', array( 1333 'mode' => cache_store::MODE_SESSION, 1334 'component' => 'phpunit', 1335 'area' => 'disabletest2' 1336 )); 1337 $instance->phpunit_add_definition('phpunit/disabletest3', array( 1338 'mode' => cache_store::MODE_REQUEST, 1339 'component' => 'phpunit', 1340 'area' => 'disabletest3' 1341 )); 1342 1343 $caches = array( 1344 'disabletest1' => cache::make('phpunit', 'disabletest1'), 1345 'disabletest2' => cache::make('phpunit', 'disabletest2'), 1346 'disabletest3' => cache::make('phpunit', 'disabletest3') 1347 ); 1348 1349 $this->assertInstanceOf(cache_phpunit_application::class, $caches['disabletest1']); 1350 $this->assertInstanceOf(cache_phpunit_session::class, $caches['disabletest2']); 1351 $this->assertInstanceOf(cache_phpunit_request::class, $caches['disabletest3']); 1352 1353 $this->assertEquals('cachestore_file', $caches['disabletest1']->phpunit_get_store_class()); 1354 $this->assertEquals('cachestore_session', $caches['disabletest2']->phpunit_get_store_class()); 1355 $this->assertEquals('cachestore_static', $caches['disabletest3']->phpunit_get_store_class()); 1356 1357 foreach ($caches as $cache) { 1358 $this->assertFalse($cache->get('test')); 1359 $this->assertTrue($cache->set('test', 'test')); 1360 $this->assertEquals('test', $cache->get('test')); 1361 } 1362 1363 cache_factory::disable_stores(); 1364 1365 $caches = array( 1366 'disabletest1' => cache::make('phpunit', 'disabletest1'), 1367 'disabletest2' => cache::make('phpunit', 'disabletest2'), 1368 'disabletest3' => cache::make('phpunit', 'disabletest3') 1369 ); 1370 1371 $this->assertInstanceOf(cache_phpunit_application::class, $caches['disabletest1']); 1372 $this->assertInstanceOf(cache_phpunit_session::class, $caches['disabletest2']); 1373 $this->assertInstanceOf(cache_phpunit_request::class, $caches['disabletest3']); 1374 1375 $this->assertEquals('cachestore_dummy', $caches['disabletest1']->phpunit_get_store_class()); 1376 $this->assertEquals('cachestore_dummy', $caches['disabletest2']->phpunit_get_store_class()); 1377 $this->assertEquals('cachestore_dummy', $caches['disabletest3']->phpunit_get_store_class()); 1378 1379 foreach ($caches as $cache) { 1380 $this->assertFalse($cache->get('test')); 1381 $this->assertTrue($cache->set('test', 'test')); 1382 $this->assertEquals('test', $cache->get('test')); 1383 } 1384 } 1385 1386 /** 1387 * Test disabling the cache. 1388 */ 1389 public function test_disable() { 1390 global $CFG; 1391 1392 if ((defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') && TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) || !empty($CFG->altcacheconfigpath)) { 1393 // We can't run this test as it requires us to delete the cache configuration script which we just 1394 // cant do with a custom path in play. 1395 $this->markTestSkipped('Skipped testing cache disable functionality as alt cache path is being used.'); 1396 } 1397 1398 $configfile = $CFG->dataroot.'/muc/config.php'; 1399 1400 // The config file will not exist yet as we've not done anything with the cache. 1401 // reset_all_data removes the file and without a call to create a configuration it doesn't exist 1402 // as yet. 1403 $this->assertFileDoesNotExist($configfile); 1404 1405 // Disable the cache 1406 cache_phpunit_factory::phpunit_disable(); 1407 1408 // Check we get the expected disabled factory. 1409 $factory = cache_factory::instance(); 1410 $this->assertInstanceOf(cache_factory_disabled::class, $factory); 1411 1412 // Check we get the expected disabled config. 1413 $config = $factory->create_config_instance(); 1414 $this->assertInstanceOf(cache_config_disabled::class, $config); 1415 1416 // Check we get the expected disabled caches. 1417 $cache = cache::make('core', 'string'); 1418 $this->assertInstanceOf(cache_disabled::class, $cache); 1419 1420 // Test an application cache. 1421 $cache = cache::make_from_params(cache_store::MODE_APPLICATION, 'phpunit', 'disable'); 1422 $this->assertInstanceOf(cache_disabled::class, $cache); 1423 1424 $this->assertFalse($cache->get('test')); 1425 $this->assertFalse($cache->get_versioned('v', 1)); 1426 $this->assertFalse($cache->set('test', 'test')); 1427 $this->assertFalse($cache->set_versioned('v', 1, 'data')); 1428 $this->assertFalse($cache->delete('test')); 1429 $this->assertTrue($cache->purge()); 1430 // Checking a lock should always report that we have one. 1431 // Acquiring or releasing a lock should always report success. 1432 $this->assertTrue($cache->check_lock_state('test')); 1433 $this->assertTrue($cache->acquire_lock('test')); 1434 $this->assertTrue($cache->acquire_lock('test')); 1435 $this->assertTrue($cache->check_lock_state('test')); 1436 $this->assertTrue($cache->release_lock('test')); 1437 $this->assertTrue($cache->release_lock('test')); 1438 $this->assertTrue($cache->check_lock_state('test')); 1439 1440 // Test a session cache. 1441 $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'disable'); 1442 $this->assertInstanceOf(cache_disabled::class, $cache); 1443 1444 $this->assertFalse($cache->get('test')); 1445 $this->assertFalse($cache->get_versioned('v', 1)); 1446 $this->assertFalse($cache->set('test', 'test')); 1447 $this->assertFalse($cache->set_versioned('v', 1, 'data')); 1448 $this->assertFalse($cache->delete('test')); 1449 $this->assertTrue($cache->purge()); 1450 1451 // Finally test a request cache. 1452 $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'phpunit', 'disable'); 1453 $this->assertInstanceOf(cache_disabled::class, $cache); 1454 1455 $this->assertFalse($cache->get('test')); 1456 $this->assertFalse($cache->get_versioned('v', 1)); 1457 $this->assertFalse($cache->set('test', 'test')); 1458 $this->assertFalse($cache->set_versioned('v', 1, 'data')); 1459 $this->assertFalse($cache->delete('test')); 1460 $this->assertTrue($cache->purge()); 1461 1462 cache_factory::reset(); 1463 1464 $factory = cache_factory::instance(true); 1465 $config = $factory->create_config_instance(); 1466 $this->assertEquals('cache_config_testing', get_class($config)); 1467 } 1468 1469 /** 1470 * Test that multiple application loaders work ok. 1471 */ 1472 public function test_multiple_application_loaders() { 1473 $instance = cache_config_testing::instance(true); 1474 $instance->phpunit_add_file_store('phpunittest1'); 1475 $instance->phpunit_add_file_store('phpunittest2'); 1476 $instance->phpunit_add_definition('phpunit/multi_loader', array( 1477 'mode' => cache_store::MODE_APPLICATION, 1478 'component' => 'phpunit', 1479 'area' => 'multi_loader' 1480 )); 1481 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3); 1482 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2); 1483 1484 $cache = cache::make('phpunit', 'multi_loader'); 1485 $this->assertInstanceOf(cache_application::class, $cache); 1486 $this->assertFalse($cache->get('test')); 1487 $this->assertTrue($cache->set('test', 'test')); 1488 $this->assertEquals('test', $cache->get('test')); 1489 $this->assertTrue($cache->delete('test')); 1490 $this->assertFalse($cache->get('test')); 1491 $this->assertTrue($cache->set('test', 'test')); 1492 $this->assertTrue($cache->purge()); 1493 $this->assertFalse($cache->get('test')); 1494 1495 // Test the many commands. 1496 $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); 1497 $result = $cache->get_many(array('a', 'b', 'c')); 1498 $this->assertIsArray($result); 1499 $this->assertCount(3, $result); 1500 $this->assertArrayHasKey('a', $result); 1501 $this->assertArrayHasKey('b', $result); 1502 $this->assertArrayHasKey('c', $result); 1503 $this->assertEquals('A', $result['a']); 1504 $this->assertEquals('B', $result['b']); 1505 $this->assertEquals('C', $result['c']); 1506 $this->assertEquals($result, $cache->get_many(array('a', 'b', 'c'))); 1507 $this->assertEquals(2, $cache->delete_many(array('a', 'c'))); 1508 $result = $cache->get_many(array('a', 'b', 'c')); 1509 $this->assertIsArray($result); 1510 $this->assertCount(3, $result); 1511 $this->assertArrayHasKey('a', $result); 1512 $this->assertArrayHasKey('b', $result); 1513 $this->assertArrayHasKey('c', $result); 1514 $this->assertFalse($result['a']); 1515 $this->assertEquals('B', $result['b']); 1516 $this->assertFalse($result['c']); 1517 1518 // Test non-recursive deletes. 1519 $this->assertTrue($cache->set('test', 'test')); 1520 $this->assertSame('test', $cache->get('test')); 1521 $this->assertTrue($cache->delete('test', false)); 1522 // We should still have it on a deeper loader. 1523 $this->assertSame('test', $cache->get('test')); 1524 // Test non-recusive with many functions. 1525 $this->assertSame(3, $cache->set_many(array( 1526 'one' => 'one', 1527 'two' => 'two', 1528 'three' => 'three' 1529 ))); 1530 $this->assertSame('one', $cache->get('one')); 1531 $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); 1532 $this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false)); 1533 $this->assertSame('one', $cache->get('one')); 1534 $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); 1535 } 1536 1537 /** 1538 * Data provider to try using a TTL or non-TTL cache. 1539 * 1540 * @return array 1541 */ 1542 public function ttl_or_not(): array { 1543 return [[false], [true]]; 1544 } 1545 1546 /** 1547 * Data provider to try using a TTL or non-TTL cache, and static acceleration or not. 1548 * 1549 * @return array 1550 */ 1551 public function ttl_and_static_acceleration_or_not(): array { 1552 return [[false, false], [false, true], [true, false], [true, true]]; 1553 } 1554 1555 /** 1556 * Data provider to try using a TTL or non-TTL cache, and simple data on or off. 1557 * 1558 * @return array 1559 */ 1560 public function ttl_and_simple_data_or_not(): array { 1561 // Same values as for ttl and static acceleration (two booleans). 1562 return $this->ttl_and_static_acceleration_or_not(); 1563 } 1564 1565 /** 1566 * Shared code to set up a two or three-layer versioned cache for testing. 1567 * 1568 * @param bool $ttl If true, sets TTL in the definition 1569 * @param bool $threelayer If true, uses a 3-layer instead of 2-layer cache 1570 * @param bool $staticacceleration If true, enables static acceleration 1571 * @param bool $simpledata If true, enables simple data 1572 * @return \cache_application Cache 1573 */ 1574 protected function create_versioned_cache(bool $ttl, bool $threelayer = false, 1575 bool $staticacceleration = false, bool $simpledata = false): \cache_application { 1576 $instance = cache_config_testing::instance(true); 1577 $instance->phpunit_add_file_store('a', false); 1578 $instance->phpunit_add_file_store('b', false); 1579 if ($threelayer) { 1580 $instance->phpunit_add_file_store('c', false); 1581 } 1582 $defarray = [ 1583 'mode' => cache_store::MODE_APPLICATION, 1584 'component' => 'phpunit', 1585 'area' => 'multi_loader' 1586 ]; 1587 if ($ttl) { 1588 $defarray['ttl'] = '600'; 1589 } 1590 if ($staticacceleration) { 1591 $defarray['staticacceleration'] = true; 1592 $defarray['staticaccelerationsize'] = 10; 1593 } 1594 if ($simpledata) { 1595 $defarray['simpledata'] = true; 1596 } 1597 $instance->phpunit_add_definition('phpunit/multi_loader', $defarray, false); 1598 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'a', 1); 1599 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'b', 2); 1600 if ($threelayer) { 1601 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'c', 3); 1602 } 1603 1604 $multicache = cache::make('phpunit', 'multi_loader'); 1605 return $multicache; 1606 } 1607 1608 /** 1609 * Tests basic use of versioned cache. 1610 * 1611 * @dataProvider ttl_and_simple_data_or_not 1612 * @param bool $ttl If true, uses a TTL cache. 1613 * @param bool $simpledata If true, turns on simple data flag 1614 * @covers ::set_versioned 1615 * @covers ::get_versioned 1616 */ 1617 public function test_versioned_cache_basic(bool $ttl, bool $simpledata): void { 1618 $multicache = $this->create_versioned_cache($ttl, false, false, $simpledata); 1619 1620 $this->assertTrue($multicache->set_versioned('game', 1, 'Pooh-sticks')); 1621 1622 $result = $multicache->get_versioned('game', 1, IGNORE_MISSING, $actualversion); 1623 $this->assertEquals('Pooh-sticks', $result); 1624 $this->assertEquals(1, $actualversion); 1625 } 1626 1627 /** 1628 * Tests versioned cache with objects. 1629 * 1630 * @dataProvider ttl_and_static_acceleration_or_not 1631 * @param bool $ttl If true, uses a TTL cache. 1632 * @param bool $staticacceleration If true, enables static acceleration 1633 * @covers ::set_versioned 1634 * @covers ::get_versioned 1635 */ 1636 public function test_versioned_cache_objects(bool $ttl, bool $staticacceleration): void { 1637 $multicache = $this->create_versioned_cache($ttl, false, $staticacceleration); 1638 1639 // Set an object value. 1640 $data = (object)['game' => 'Pooh-sticks']; 1641 $this->assertTrue($multicache->set_versioned('game', 1, $data)); 1642 1643 // Get it. 1644 $result = $multicache->get_versioned('game', 1); 1645 $this->assertEquals('Pooh-sticks', $result->game); 1646 1647 // Mess about with the value in the returned object. 1648 $result->game = 'Tag'; 1649 1650 // Get it again and confirm the cached object has not been affected. 1651 $result = $multicache->get_versioned('game', 1); 1652 $this->assertEquals('Pooh-sticks', $result->game); 1653 } 1654 1655 /** 1656 * Tests requesting a version that doesn't exist. 1657 * 1658 * @dataProvider ttl_or_not 1659 * @param bool $ttl If true, uses a TTL cache. 1660 * @covers ::set_versioned 1661 * @covers ::get_versioned 1662 */ 1663 public function test_versioned_cache_not_exist(bool $ttl): void { 1664 $multicache = $this->create_versioned_cache($ttl); 1665 1666 $multicache->set_versioned('game', 1, 'Pooh-sticks'); 1667 1668 // Exists but with wrong version. 1669 $this->assertFalse($multicache->get_versioned('game', 2)); 1670 1671 // Doesn't exist at all. 1672 $this->assertFalse($multicache->get_versioned('frog', 0)); 1673 } 1674 1675 /** 1676 * Tests attempts to use get after set_version or get_version after set. 1677 * 1678 * @dataProvider ttl_or_not 1679 * @param bool $ttl If true, uses a TTL cache. 1680 * @covers ::set_versioned 1681 * @covers ::get_versioned 1682 */ 1683 public function test_versioned_cache_incompatible_versioning(bool $ttl): void { 1684 $multicache = $this->create_versioned_cache($ttl); 1685 1686 // What if you use get on a get_version cache? 1687 $multicache->set_versioned('game', 1, 'Pooh-sticks'); 1688 try { 1689 $multicache->get('game'); 1690 $this->fail(); 1691 } catch (\coding_exception $e) { 1692 $this->assertStringContainsString('Unexpectedly found versioned cache entry', $e->getMessage()); 1693 } 1694 1695 // Or get_version on a get cache? 1696 $multicache->set('toy', 'Train set'); 1697 try { 1698 $multicache->get_versioned('toy', 1); 1699 $this->fail(); 1700 } catch (\coding_exception $e) { 1701 $this->assertStringContainsString('Unexpectedly found non-versioned cache entry', $e->getMessage()); 1702 } 1703 } 1704 1705 /** 1706 * Versions are only stored once, so if you set a newer version you will always get it even 1707 * if you ask for the lower version number. 1708 * 1709 * @dataProvider ttl_or_not 1710 * @param bool $ttl If true, uses a TTL cache. 1711 * @covers ::set_versioned 1712 * @covers ::get_versioned 1713 */ 1714 public function test_versioned_cache_single_copy(bool $ttl): void { 1715 $multicache = $this->create_versioned_cache($ttl); 1716 1717 $multicache->set_versioned('game', 1, 'Pooh-sticks'); 1718 $multicache->set_versioned('game', 2, 'Tag'); 1719 $this->assertEquals('Tag', $multicache->get_versioned('game', 1, IGNORE_MISSING, $actualversion)); 1720 1721 // The reported version number matches the one returned, not requested. 1722 $this->assertEquals(2, $actualversion); 1723 } 1724 1725 /** 1726 * If the first (local) store has an outdated copy but the second (shared) store has a newer 1727 * one, then it should automatically be retrieved. 1728 * 1729 * @dataProvider ttl_or_not 1730 * @param bool $ttl If true, uses a TTL cache. 1731 * @covers ::set_versioned 1732 * @covers ::get_versioned 1733 */ 1734 public function test_versioned_cache_outdated_local(bool $ttl): void { 1735 $multicache = $this->create_versioned_cache($ttl); 1736 1737 // Set initial value to version 2, 'Tag', in both stores. 1738 $multicache->set_versioned('game', 2, 'Tag'); 1739 1740 // Get the two separate cache stores for the multi-level cache. 1741 $factory = cache_factory::instance(); 1742 $definition = $factory->create_definition('phpunit', 'multi_loader'); 1743 [0 => $storea, 1 => $storeb] = $factory->get_store_instances_in_use($definition); 1744 1745 // Simulate what happens if the shared cache is updated with a new version but the 1746 // local one still has an old version. 1747 $hashgame = cache_helper::hash_key('game', $definition); 1748 $data = 'British Bulldog'; 1749 if ($ttl) { 1750 $data = new \cache_ttl_wrapper($data, 600); 1751 } 1752 $storeb->set($hashgame, new \core_cache\version_wrapper($data, 3)); 1753 1754 // If we ask for the old one we'll get it straight off from local cache. 1755 $this->assertEquals('Tag', $multicache->get_versioned('game', 2)); 1756 1757 // But if we ask for the new one it will still get it via the shared cache. 1758 $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 3)); 1759 1760 // Also, now it will have been updated in the local cache as well. 1761 $localvalue = $storea->get($hashgame); 1762 if ($ttl) { 1763 // In case the time has changed slightly since the first set, we can't do an exact 1764 // compare, so check it ignoring the time field. 1765 $this->assertEquals(3, $localvalue->version); 1766 $ttldata = $localvalue->data; 1767 $this->assertInstanceOf('cache_ttl_wrapper', $ttldata); 1768 $this->assertEquals('British Bulldog', $ttldata->data); 1769 } else { 1770 $this->assertEquals(new \core_cache\version_wrapper('British Bulldog', 3), $localvalue); 1771 } 1772 } 1773 1774 /** 1775 * When we request a newer version, older ones are automatically deleted in every level of the 1776 * cache (to save I/O if there are multiple requests, as if there is another request it will 1777 * not have to retrieve the values to find out that they're old). 1778 * 1779 * @dataProvider ttl_or_not 1780 * @param bool $ttl If true, uses a TTL cache. 1781 * @covers ::set_versioned 1782 * @covers ::get_versioned 1783 */ 1784 public function test_versioned_cache_deleting_outdated(bool $ttl): void { 1785 $multicache = $this->create_versioned_cache($ttl); 1786 1787 // Set initial value to version 2, 'Tag', in both stores. 1788 $multicache->set_versioned('game', 2, 'Tag'); 1789 1790 // Get the two separate cache stores for the multi-level cache. 1791 $factory = cache_factory::instance(); 1792 $definition = $factory->create_definition('phpunit', 'multi_loader'); 1793 [0 => $storea, 1 => $storeb] = $factory->get_store_instances_in_use($definition); 1794 1795 // If we request a newer version, then any older version should be deleted in each 1796 // cache level. 1797 $this->assertFalse($multicache->get_versioned('game', 4)); 1798 $hashgame = cache_helper::hash_key('game', $definition); 1799 $this->assertFalse($storea->get($hashgame)); 1800 $this->assertFalse($storeb->get($hashgame)); 1801 } 1802 1803 /** 1804 * Tests a versioned cache when using static cache. 1805 * 1806 * @covers ::set_versioned 1807 * @covers ::get_versioned 1808 */ 1809 public function test_versioned_cache_static(): void { 1810 $staticcache = $this->create_versioned_cache(false, false, true); 1811 1812 // Set a value in the cache, version 1. This will store it in static acceleration. 1813 $staticcache->set_versioned('game', 1, 'Pooh-sticks'); 1814 1815 // Get the first cache store (we don't need the second one for this test). 1816 $factory = cache_factory::instance(); 1817 $definition = $factory->create_definition('phpunit', 'multi_loader'); 1818 [0 => $storea] = $factory->get_store_instances_in_use($definition); 1819 1820 // Hack a newer version into cache store without directly calling set (now the static 1821 // has v1, store has v2). This simulates another client updating the cache. 1822 $hashgame = cache_helper::hash_key('game', $definition); 1823 $storea->set($hashgame, new \core_cache\version_wrapper('Tag', 2)); 1824 1825 // Get the key from the cache, v1. This will use static acceleration. 1826 $this->assertEquals('Pooh-sticks', $staticcache->get_versioned('game', 1)); 1827 1828 // Now if we ask for a newer version, it should not use the static cached one. 1829 $this->assertEquals('Tag', $staticcache->get_versioned('game', 2)); 1830 1831 // This get should have updated static acceleration, so it will be used next time without 1832 // a store request. 1833 $storea->set($hashgame, new \core_cache\version_wrapper('British Bulldog', 3)); 1834 $this->assertEquals('Tag', $staticcache->get_versioned('game', 2)); 1835 1836 // Requesting the higher version will get rid of static acceleration again. 1837 $this->assertEquals('British Bulldog', $staticcache->get_versioned('game', 3)); 1838 1839 // Finally ask for a version that doesn't exist anywhere, just to confirm it returns null. 1840 $this->assertFalse($staticcache->get_versioned('game', 4)); 1841 } 1842 1843 /** 1844 * Tests basic use of 3-layer versioned caches. 1845 * 1846 * @covers ::set_versioned 1847 * @covers ::get_versioned 1848 */ 1849 public function test_versioned_cache_3_layers_basic(): void { 1850 $multicache = $this->create_versioned_cache(false, true); 1851 1852 // Basic use of set_versioned and get_versioned. 1853 $multicache->set_versioned('game', 1, 'Pooh-sticks'); 1854 $this->assertEquals('Pooh-sticks', $multicache->get_versioned('game', 1)); 1855 1856 // What if you ask for a version that doesn't exist? 1857 $this->assertFalse($multicache->get_versioned('game', 2)); 1858 1859 // Setting a new version wipes out the old version; if you request it, you get the new one. 1860 $multicache->set_versioned('game', 2, 'Tag'); 1861 $this->assertEquals('Tag', $multicache->get_versioned('game', 1)); 1862 } 1863 1864 /** 1865 * Tests use of 3-layer versioned caches where the 3 layers currently have different versions. 1866 * 1867 * @covers ::set_versioned 1868 * @covers ::get_versioned 1869 */ 1870 public function test_versioned_cache_3_layers_different_data(): void { 1871 // Set version 2 using normal method. 1872 $multicache = $this->create_versioned_cache(false, true); 1873 $multicache->set_versioned('game', 2, 'Tag'); 1874 1875 // Get the three separate cache stores for the multi-level cache. 1876 $factory = cache_factory::instance(); 1877 $definition = $factory->create_definition('phpunit', 'multi_loader'); 1878 [0 => $storea, 1 => $storeb, 2 => $storec] = $factory->get_store_instances_in_use($definition); 1879 1880 // Set up two other versions so every level has a different version. 1881 $hashgame = cache_helper::hash_key('game', $definition); 1882 $storeb->set($hashgame, new \core_cache\version_wrapper('British Bulldog', 3)); 1883 $storec->set($hashgame, new \core_cache\version_wrapper('Hopscotch', 4)); 1884 1885 // First request can be satisfied from A; second request requires B... 1886 $this->assertEquals('Tag', $multicache->get_versioned('game', 2)); 1887 $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 3)); 1888 1889 // And should update the data in A. 1890 $this->assertEquals(new \core_cache\version_wrapper('British Bulldog', 3), $storea->get($hashgame)); 1891 $this->assertEquals('British Bulldog', $multicache->get_versioned('game', 1)); 1892 1893 // But newer data should still be in C. 1894 $this->assertEquals('Hopscotch', $multicache->get_versioned('game', 4)); 1895 // Now it's stored in A and B too. 1896 $this->assertEquals(new \core_cache\version_wrapper('Hopscotch', 4), $storea->get($hashgame)); 1897 $this->assertEquals(new \core_cache\version_wrapper('Hopscotch', 4), $storeb->get($hashgame)); 1898 } 1899 1900 /** 1901 * Test that multiple application loaders work ok. 1902 */ 1903 public function test_multiple_session_loaders() { 1904 /* @var cache_config_testing $instance */ 1905 $instance = cache_config_testing::instance(true); 1906 $instance->phpunit_add_session_store('phpunittest1'); 1907 $instance->phpunit_add_session_store('phpunittest2'); 1908 $instance->phpunit_add_definition('phpunit/multi_loader', array( 1909 'mode' => cache_store::MODE_SESSION, 1910 'component' => 'phpunit', 1911 'area' => 'multi_loader' 1912 )); 1913 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest1', 3); 1914 $instance->phpunit_add_definition_mapping('phpunit/multi_loader', 'phpunittest2', 2); 1915 1916 $cache = cache::make('phpunit', 'multi_loader'); 1917 $this->assertInstanceOf(cache_session::class, $cache); 1918 $this->assertFalse($cache->get('test')); 1919 $this->assertTrue($cache->set('test', 'test')); 1920 $this->assertEquals('test', $cache->get('test')); 1921 $this->assertTrue($cache->delete('test')); 1922 $this->assertFalse($cache->get('test')); 1923 $this->assertTrue($cache->set('test', 'test')); 1924 $this->assertTrue($cache->purge()); 1925 $this->assertFalse($cache->get('test')); 1926 1927 // Test the many commands. 1928 $this->assertEquals(3, $cache->set_many(array('a' => 'A', 'b' => 'B', 'c' => 'C'))); 1929 $result = $cache->get_many(array('a', 'b', 'c')); 1930 $this->assertIsArray($result); 1931 $this->assertCount(3, $result); 1932 $this->assertArrayHasKey('a', $result); 1933 $this->assertArrayHasKey('b', $result); 1934 $this->assertArrayHasKey('c', $result); 1935 $this->assertEquals('A', $result['a']); 1936 $this->assertEquals('B', $result['b']); 1937 $this->assertEquals('C', $result['c']); 1938 $this->assertEquals($result, $cache->get_many(array('a', 'b', 'c'))); 1939 $this->assertEquals(2, $cache->delete_many(array('a', 'c'))); 1940 $result = $cache->get_many(array('a', 'b', 'c')); 1941 $this->assertIsArray($result); 1942 $this->assertCount(3, $result); 1943 $this->assertArrayHasKey('a', $result); 1944 $this->assertArrayHasKey('b', $result); 1945 $this->assertArrayHasKey('c', $result); 1946 $this->assertFalse($result['a']); 1947 $this->assertEquals('B', $result['b']); 1948 $this->assertFalse($result['c']); 1949 1950 // Test non-recursive deletes. 1951 $this->assertTrue($cache->set('test', 'test')); 1952 $this->assertSame('test', $cache->get('test')); 1953 $this->assertTrue($cache->delete('test', false)); 1954 // We should still have it on a deeper loader. 1955 $this->assertSame('test', $cache->get('test')); 1956 // Test non-recusive with many functions. 1957 $this->assertSame(3, $cache->set_many(array( 1958 'one' => 'one', 1959 'two' => 'two', 1960 'three' => 'three' 1961 ))); 1962 $this->assertSame('one', $cache->get('one')); 1963 $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); 1964 $this->assertSame(3, $cache->delete_many(array('one', 'two', 'three'), false)); 1965 $this->assertSame('one', $cache->get('one')); 1966 $this->assertSame(array('two' => 'two', 'three' => 'three'), $cache->get_many(array('two', 'three'))); 1967 } 1968 1969 /** 1970 * Test switching users with session caches. 1971 */ 1972 public function test_session_cache_switch_user() { 1973 $this->resetAfterTest(true); 1974 $cache = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache'); 1975 $user1 = $this->getDataGenerator()->create_user(); 1976 $user2 = $this->getDataGenerator()->create_user(); 1977 1978 // Log in as the first user. 1979 $this->setUser($user1); 1980 $sesskey1 = sesskey(); 1981 1982 // Set a basic value in the cache. 1983 $cache->set('var', 1); 1984 $this->assertTrue($cache->has('var')); 1985 $this->assertEquals(1, $cache->get('var')); 1986 1987 // Change to the second user. 1988 $this->setUser($user2); 1989 $sesskey2 = sesskey(); 1990 1991 // Make sure the cache doesn't give us the data for the last user. 1992 $this->assertNotEquals($sesskey1, $sesskey2); 1993 $this->assertFalse($cache->has('var')); 1994 $this->assertEquals(false, $cache->get('var')); 1995 } 1996 1997 /** 1998 * Test switching users with session caches. 1999 */ 2000 public function test_session_cache_switch_user_application_mapping() { 2001 $this->resetAfterTest(true); 2002 $instance = cache_config_testing::instance(true); 2003 $instance->phpunit_add_file_store('testfilestore'); 2004 $instance->phpunit_add_definition('phpunit/testappsession', array( 2005 'mode' => cache_store::MODE_SESSION, 2006 'component' => 'phpunit', 2007 'area' => 'testappsession' 2008 )); 2009 $instance->phpunit_add_definition_mapping('phpunit/testappsession', 'testfilestore', 3); 2010 $cache = cache::make('phpunit', 'testappsession'); 2011 $user1 = $this->getDataGenerator()->create_user(); 2012 $user2 = $this->getDataGenerator()->create_user(); 2013 2014 // Log in as the first user. 2015 $this->setUser($user1); 2016 $sesskey1 = sesskey(); 2017 2018 // Set a basic value in the cache. 2019 $cache->set('var', 1); 2020 $this->assertTrue($cache->has('var')); 2021 $this->assertEquals(1, $cache->get('var')); 2022 2023 // Change to the second user. 2024 $this->setUser($user2); 2025 $sesskey2 = sesskey(); 2026 2027 // Make sure the cache doesn't give us the data for the last user. 2028 $this->assertNotEquals($sesskey1, $sesskey2); 2029 $this->assertFalse($cache->has('var')); 2030 $this->assertEquals(false, $cache->get('var')); 2031 } 2032 2033 /** 2034 * Test two session caches being used at once to confirm collisions don't occur. 2035 */ 2036 public function test_dual_session_caches() { 2037 $instance = cache_config_testing::instance(true); 2038 $instance->phpunit_add_definition('phpunit/testsess1', array( 2039 'mode' => cache_store::MODE_SESSION, 2040 'component' => 'phpunit', 2041 'area' => 'testsess1' 2042 )); 2043 $instance->phpunit_add_definition('phpunit/testsess2', array( 2044 'mode' => cache_store::MODE_SESSION, 2045 'component' => 'phpunit', 2046 'area' => 'testsess2' 2047 )); 2048 $cache1 = cache::make('phpunit', 'testsess1'); 2049 $cache2 = cache::make('phpunit', 'testsess2'); 2050 2051 $this->assertFalse($cache1->has('test')); 2052 $this->assertFalse($cache2->has('test')); 2053 2054 $this->assertTrue($cache1->set('test', '1')); 2055 2056 $this->assertTrue($cache1->has('test')); 2057 $this->assertFalse($cache2->has('test')); 2058 2059 $this->assertTrue($cache2->set('test', '2')); 2060 2061 $this->assertEquals(1, $cache1->get('test')); 2062 $this->assertEquals(2, $cache2->get('test')); 2063 2064 $this->assertTrue($cache1->delete('test')); 2065 } 2066 2067 /** 2068 * Test multiple session caches when switching user. 2069 */ 2070 public function test_session_cache_switch_user_multiple() { 2071 $this->resetAfterTest(true); 2072 $cache1 = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache1'); 2073 $cache2 = cache::make_from_params(cache_store::MODE_SESSION, 'phpunit', 'sessioncache2'); 2074 $user1 = $this->getDataGenerator()->create_user(); 2075 $user2 = $this->getDataGenerator()->create_user(); 2076 2077 // Log in as the first user. 2078 $this->setUser($user1); 2079 $sesskey1 = sesskey(); 2080 2081 // Set a basic value in the caches. 2082 $cache1->set('var', 1); 2083 $cache2->set('var', 2); 2084 $this->assertEquals(1, $cache1->get('var')); 2085 $this->assertEquals(2, $cache2->get('var')); 2086 2087 // Change to the second user. 2088 $this->setUser($user2); 2089 $sesskey2 = sesskey(); 2090 2091 // Make sure the cache doesn't give us the data for the last user. 2092 // Also make sure that switching the user has lead to both caches being purged. 2093 $this->assertNotEquals($sesskey1, $sesskey2); 2094 $this->assertEquals(false, $cache1->get('var')); 2095 $this->assertEquals(false, $cache2->get('var')); 2096 } 2097 2098 /** 2099 * Test application locking. 2100 */ 2101 public function test_application_locking() { 2102 $instance = cache_config_testing::instance(true); 2103 $instance->phpunit_add_definition('phpunit/test_application_locking', array( 2104 'mode' => cache_store::MODE_APPLICATION, 2105 'component' => 'phpunit', 2106 'area' => 'test_application_locking', 2107 'staticacceleration' => true, 2108 'staticaccelerationsize' => 1, 2109 'requirelockingread' => true, 2110 'requirelockingwrite' => true 2111 )); 2112 $cache = cache::make('phpunit', 'test_application_locking'); 2113 $this->assertInstanceOf(cache_application::class, $cache); 2114 2115 $this->assertTrue($cache->set('a', 'A')); 2116 $this->assertTrue($cache->set('b', 'B')); 2117 $this->assertTrue($cache->set('c', 'C')); 2118 $this->assertEquals('A', $cache->get('a')); 2119 $this->assertEquals(array('b' => 'B', 'c' => 'C'), $cache->get_many(array('b', 'c'))); 2120 $this->assertTrue($cache->delete('a')); 2121 $this->assertFalse($cache->has('a')); 2122 } 2123 2124 /** 2125 * The application locking feature should work with caches that support multiple identifiers 2126 * (static cache and MongoDB with a specific setting). 2127 * 2128 * @covers \cache_application 2129 */ 2130 public function test_application_locking_multiple_identifier_cache() { 2131 // Get an arbitrary definition (modinfo). 2132 $instance = cache_config_testing::instance(true); 2133 $definitions = $instance->get_definitions(); 2134 $definition = \cache_definition::load('phpunit', $definitions['core/coursemodinfo']); 2135 2136 // Set up a static cache using that definition, wrapped in cache_application so we can do 2137 // locking. 2138 $store = new \cachestore_static('test'); 2139 $store->initialise($definition); 2140 $cache = new cache_application($definition, $store); 2141 2142 // Test the three locking functions. 2143 $cache->acquire_lock('frog'); 2144 $this->assertTrue($cache->check_lock_state('frog')); 2145 $cache->release_lock('frog'); 2146 } 2147 2148 /** 2149 * Test requiring a lock before attempting to set a key. 2150 * 2151 * @covers ::set_implementation 2152 */ 2153 public function test_application_locking_before_write() { 2154 $instance = cache_config_testing::instance(true); 2155 $instance->phpunit_add_definition('phpunit/test_application_locking', array( 2156 'mode' => cache_store::MODE_APPLICATION, 2157 'component' => 'phpunit', 2158 'area' => 'test_application_locking', 2159 'staticacceleration' => true, 2160 'staticaccelerationsize' => 1, 2161 'requirelockingbeforewrite' => true 2162 )); 2163 $cache = cache::make('phpunit', 'test_application_locking'); 2164 $this->assertInstanceOf(cache_application::class, $cache); 2165 2166 $cache->acquire_lock('a'); 2167 $this->assertTrue($cache->set('a', 'A')); 2168 $cache->release_lock('a'); 2169 2170 $this->expectExceptionMessage('Attempted to set cache key "b" without a lock. ' 2171 . 'Locking before writes is required for phpunit/test_application_locking'); 2172 $this->assertFalse($cache->set('b', 'B')); 2173 } 2174 2175 2176 /** 2177 * Test that invalid lock setting combinations are caught. 2178 * 2179 * @covers ::make 2180 */ 2181 public function test_application_conflicting_locks() { 2182 $instance = cache_config_testing::instance(true); 2183 $instance->phpunit_add_definition('phpunit/test_application_locking', array( 2184 'mode' => cache_store::MODE_APPLICATION, 2185 'component' => 'phpunit', 2186 'area' => 'test_application_locking', 2187 'staticacceleration' => true, 2188 'staticaccelerationsize' => 1, 2189 'requirelockingwrite' => true, 2190 'requirelockingbeforewrite' => true, 2191 )); 2192 2193 $this->expectException('coding_exception'); 2194 cache::make('phpunit', 'test_application_locking'); 2195 } 2196 2197 /** 2198 * Test that locking before write works when writing across multiple layers. 2199 * 2200 * @covers \cache_loader 2201 * @return void 2202 */ 2203 public function test_application_locking_multiple_layers() { 2204 2205 $instance = cache_config_testing::instance(true); 2206 $instance->phpunit_add_definition('phpunit/test_application_locking', array( 2207 'mode' => cache_store::MODE_APPLICATION, 2208 'component' => 'phpunit', 2209 'area' => 'test_application_locking', 2210 'staticacceleration' => true, 2211 'staticaccelerationsize' => 1, 2212 'requirelockingbeforewrite' => true 2213 ), false); 2214 $instance->phpunit_add_file_store('phpunittest1'); 2215 $instance->phpunit_add_file_store('phpunittest2'); 2216 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest1', 1); 2217 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest2', 2); 2218 2219 $cache = cache::make('phpunit', 'test_application_locking'); 2220 $this->assertInstanceOf(cache_application::class, $cache); 2221 2222 // Check that we can set a key across multiple layers. 2223 $cache->acquire_lock('a'); 2224 $this->assertTrue($cache->set('a', 'A')); 2225 $cache->release_lock('a'); 2226 2227 // Delete from the current layer. 2228 $cache->delete('a', false); 2229 2230 // Check that we can get the value from the deeper layer, which will also re-set it in the current one. 2231 $this->assertEquals('A', $cache->get('a')); 2232 2233 // Try set/delete/get_many. 2234 $cache->acquire_lock('x'); 2235 $cache->acquire_lock('y'); 2236 $cache->acquire_lock('z'); 2237 $this->assertEquals(3, $cache->set_many(['x' => 'X', 'y' => 'Y', 'z' => 'Z'])); 2238 $cache->release_lock('x'); 2239 $cache->release_lock('y'); 2240 $cache->release_lock('z'); 2241 2242 $cache->delete_many(['x', 'y', 'z'], false); 2243 $this->assertEquals(['x' => 'X', 'y' => 'Y', 'z' => 'Z'], $cache->get_many(['x', 'y', 'z'])); 2244 2245 $cache->purge(); 2246 2247 // Try the tests again with a third layer. 2248 $instance->phpunit_add_file_store('phpunittest3'); 2249 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest3', 3); 2250 $cache = cache::make('phpunit', 'test_application_locking'); 2251 $this->assertInstanceOf(cache_application::class, $cache); 2252 2253 // Check that we can set a key across multiple layers. 2254 $cache->acquire_lock('a'); 2255 $this->assertTrue($cache->set('a', 'A')); 2256 $cache->release_lock('a'); 2257 2258 // Delete from the current layer. 2259 $cache->delete('a', false); 2260 2261 // Check that we can get the value from the deeper layer, which will also re-set it in the current one. 2262 $this->assertEquals('A', $cache->get('a')); 2263 2264 // Try set/delete/get_many. 2265 $cache->acquire_lock('x'); 2266 $cache->acquire_lock('y'); 2267 $cache->acquire_lock('z'); 2268 $this->assertEquals(3, $cache->set_many(['x' => 'X', 'y' => 'Y', 'z' => 'Z'])); 2269 $cache->release_lock('x'); 2270 $cache->release_lock('y'); 2271 $cache->release_lock('z'); 2272 2273 $cache->delete_many(['x', 'y', 'z'], false); 2274 $this->assertEquals(['x' => 'X', 'y' => 'Y', 'z' => 'Z'], $cache->get_many(['x', 'y', 'z'])); 2275 } 2276 2277 /** 2278 * Tests that locking fails correctly when either layer of a 2-layer cache has a lock already. 2279 * 2280 * @covers \cache_loader 2281 */ 2282 public function test_application_locking_multiple_layers_failures(): void { 2283 2284 $instance = cache_config_testing::instance(true); 2285 $instance->phpunit_add_definition('phpunit/test_application_locking', array( 2286 'mode' => cache_store::MODE_APPLICATION, 2287 'component' => 'phpunit', 2288 'area' => 'test_application_locking', 2289 'staticacceleration' => true, 2290 'staticaccelerationsize' => 1, 2291 'requirelockingbeforewrite' => true 2292 ), false); 2293 $instance->phpunit_add_file_store('phpunittest1'); 2294 $instance->phpunit_add_file_store('phpunittest2'); 2295 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest1', 1); 2296 $instance->phpunit_add_definition_mapping('phpunit/test_application_locking', 'phpunittest2', 2); 2297 2298 $cache = cache::make('phpunit', 'test_application_locking'); 2299 2300 // We need to get the individual stores so as to set up the right behaviour here. 2301 $ref = new \ReflectionClass('\cache'); 2302 $definitionprop = $ref->getProperty('definition'); 2303 $definitionprop->setAccessible(true); 2304 $storeprop = $ref->getProperty('store'); 2305 $storeprop->setAccessible(true); 2306 $loaderprop = $ref->getProperty('loader'); 2307 $loaderprop->setAccessible(true); 2308 2309 $definition = $definitionprop->getValue($cache); 2310 $localstore = $storeprop->getValue($cache); 2311 $sharedcache = $loaderprop->getValue($cache); 2312 $sharedstore = $storeprop->getValue($sharedcache); 2313 2314 // Set the lock waiting time to 1 second so it doesn't take forever to run the test. 2315 $ref = new \ReflectionClass('\cachestore_file'); 2316 $lockwaitprop = $ref->getProperty('lockwait'); 2317 $lockwaitprop->setAccessible(true); 2318 2319 $lockwaitprop->setValue($localstore, 1); 2320 $lockwaitprop->setValue($sharedstore, 1); 2321 2322 // Get key details and the cache identifier. 2323 $hashedkey = cache_helper::hash_key('apple', $definition); 2324 $localidentifier = $cache->get_identifier(); 2325 $sharedidentifier = $sharedcache->get_identifier(); 2326 2327 // 1. Local cache is not locked but parent cache is locked. 2328 $sharedstore->acquire_lock($hashedkey, 'somebodyelse'); 2329 try { 2330 $this->assertFalse($cache->acquire_lock('apple')); 2331 2332 // Neither store is locked by us, shared store still locked. 2333 $this->assertFalse((bool)$localstore->check_lock_state($hashedkey, $localidentifier)); 2334 $this->assertFalse((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier)); 2335 $this->assertTrue((bool)$sharedstore->check_lock_state($hashedkey, 'somebodyelse')); 2336 2337 } finally { 2338 $sharedstore->release_lock($hashedkey, 'somebodyelse'); 2339 } 2340 2341 // 2. Local cache is locked, parent cache is not locked. 2342 $localstore->acquire_lock($hashedkey, 'somebodyelse'); 2343 try { 2344 $this->assertFalse($cache->acquire_lock('apple')); 2345 2346 // Neither store is locked by us, local store still locked. 2347 $this->assertFalse((bool)$localstore->check_lock_state($hashedkey, $localidentifier)); 2348 $this->assertFalse((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier)); 2349 $this->assertTrue((bool)$localstore->check_lock_state($hashedkey, 'somebodyelse')); 2350 } finally { 2351 $localstore->release_lock($hashedkey, 'somebodyelse'); 2352 } 2353 2354 // 3. Just for completion, test what happens if we do lock it. 2355 $this->assertTrue($cache->acquire_lock('apple')); 2356 try { 2357 $this->assertTrue((bool)$localstore->check_lock_state($hashedkey, $localidentifier)); 2358 $this->assertTrue((bool)$sharedstore->check_lock_state($hashedkey, $sharedidentifier)); 2359 } finally { 2360 $cache->release_lock('apple'); 2361 } 2362 } 2363 2364 /** 2365 * Test the static cache_helper method purge_stores_used_by_definition. 2366 */ 2367 public function test_purge_stores_used_by_definition() { 2368 $instance = cache_config_testing::instance(true); 2369 $instance->phpunit_add_definition('phpunit/test_purge_stores_used_by_definition', array( 2370 'mode' => cache_store::MODE_APPLICATION, 2371 'component' => 'phpunit', 2372 'area' => 'test_purge_stores_used_by_definition' 2373 )); 2374 $cache = cache::make('phpunit', 'test_purge_stores_used_by_definition'); 2375 $this->assertInstanceOf(cache_application::class, $cache); 2376 $this->assertTrue($cache->set('test', 'test')); 2377 unset($cache); 2378 2379 cache_helper::purge_stores_used_by_definition('phpunit', 'test_purge_stores_used_by_definition'); 2380 2381 $cache = cache::make('phpunit', 'test_purge_stores_used_by_definition'); 2382 $this->assertInstanceOf(cache_application::class, $cache); 2383 $this->assertFalse($cache->get('test')); 2384 } 2385 2386 /** 2387 * Test purge routines. 2388 */ 2389 public function test_purge_routines() { 2390 $instance = cache_config_testing::instance(true); 2391 $instance->phpunit_add_definition('phpunit/purge1', array( 2392 'mode' => cache_store::MODE_APPLICATION, 2393 'component' => 'phpunit', 2394 'area' => 'purge1' 2395 )); 2396 $instance->phpunit_add_definition('phpunit/purge2', array( 2397 'mode' => cache_store::MODE_APPLICATION, 2398 'component' => 'phpunit', 2399 'area' => 'purge2', 2400 'requireidentifiers' => array( 2401 'id' 2402 ) 2403 )); 2404 2405 $factory = cache_factory::instance(); 2406 $definition = $factory->create_definition('phpunit', 'purge1'); 2407 $this->assertFalse($definition->has_required_identifiers()); 2408 $cache = $factory->create_cache($definition); 2409 $this->assertInstanceOf(cache_application::class, $cache); 2410 $this->assertTrue($cache->set('test', 'test')); 2411 $this->assertTrue($cache->has('test')); 2412 cache_helper::purge_by_definition('phpunit', 'purge1'); 2413 $this->assertFalse($cache->has('test')); 2414 2415 $factory = cache_factory::instance(); 2416 $definition = $factory->create_definition('phpunit', 'purge2'); 2417 $this->assertTrue($definition->has_required_identifiers()); 2418 $cache = $factory->create_cache($definition); 2419 $this->assertInstanceOf(cache_application::class, $cache); 2420 $this->assertTrue($cache->set('test', 'test')); 2421 $this->assertTrue($cache->has('test')); 2422 cache_helper::purge_stores_used_by_definition('phpunit', 'purge2'); 2423 $this->assertFalse($cache->has('test')); 2424 2425 try { 2426 cache_helper::purge_by_definition('phpunit', 'purge2'); 2427 $this->fail('Should not be able to purge a definition required identifiers without providing them.'); 2428 } catch (\coding_exception $ex) { 2429 $this->assertStringContainsString('Identifier required for cache has not been provided', $ex->getMessage()); 2430 } 2431 } 2432 2433 /** 2434 * Tests that ad-hoc caches are correctly purged with a purge_all call. 2435 */ 2436 public function test_purge_all_with_adhoc_caches() { 2437 $cache = cache::make_from_params(cache_store::MODE_REQUEST, 'core_cache', 'test'); 2438 $cache->set('test', 123); 2439 cache_helper::purge_all(); 2440 $this->assertFalse($cache->get('test')); 2441 } 2442 2443 /** 2444 * Test that the default stores all support searching. 2445 */ 2446 public function test_defaults_support_searching() { 2447 $instance = cache_config_testing::instance(true); 2448 $instance->phpunit_add_definition('phpunit/search1', array( 2449 'mode' => cache_store::MODE_APPLICATION, 2450 'component' => 'phpunit', 2451 'area' => 'search1', 2452 'requiresearchable' => true 2453 )); 2454 $instance->phpunit_add_definition('phpunit/search2', array( 2455 'mode' => cache_store::MODE_SESSION, 2456 'component' => 'phpunit', 2457 'area' => 'search2', 2458 'requiresearchable' => true 2459 )); 2460 $instance->phpunit_add_definition('phpunit/search3', array( 2461 'mode' => cache_store::MODE_REQUEST, 2462 'component' => 'phpunit', 2463 'area' => 'search3', 2464 'requiresearchable' => true 2465 )); 2466 $factory = cache_factory::instance(); 2467 2468 // Test application cache is searchable. 2469 $definition = $factory->create_definition('phpunit', 'search1'); 2470 $this->assertInstanceOf(cache_definition::class, $definition); 2471 $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); 2472 $cache = $factory->create_cache($definition); 2473 $this->assertInstanceOf(cache_application::class, $cache); 2474 $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); 2475 2476 // Test session cache is searchable. 2477 $definition = $factory->create_definition('phpunit', 'search2'); 2478 $this->assertInstanceOf(cache_definition::class, $definition); 2479 $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); 2480 $cache = $factory->create_cache($definition); 2481 $this->assertInstanceOf(cache_session::class, $cache); 2482 $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); 2483 2484 // Test request cache is searchable. 2485 $definition = $factory->create_definition('phpunit', 'search3'); 2486 $this->assertInstanceOf(cache_definition::class, $definition); 2487 $this->assertEquals(cache_store::IS_SEARCHABLE, $definition->get_requirements_bin() & cache_store::IS_SEARCHABLE); 2488 $cache = $factory->create_cache($definition); 2489 $this->assertInstanceOf(cache_request::class, $cache); 2490 $this->assertArrayHasKey('cache_is_searchable', $cache->phpunit_get_store_implements()); 2491 } 2492 2493 /** 2494 * Test static acceleration 2495 * 2496 * Note: All the assertGreaterThanOrEqual() in this test should be assertGreaterThan() be because of some microtime() 2497 * resolution problems under some OSs / PHP versions, we are accepting equal as valid outcome. For more info see MDL-57147. 2498 */ 2499 public function test_static_acceleration() { 2500 $instance = cache_config_testing::instance(); 2501 $instance->phpunit_add_definition('phpunit/accelerated', array( 2502 'mode' => cache_store::MODE_APPLICATION, 2503 'component' => 'phpunit', 2504 'area' => 'accelerated', 2505 'staticacceleration' => true, 2506 'staticaccelerationsize' => 3, 2507 )); 2508 $instance->phpunit_add_definition('phpunit/accelerated2', array( 2509 'mode' => cache_store::MODE_APPLICATION, 2510 'component' => 'phpunit', 2511 'area' => 'accelerated2', 2512 'staticacceleration' => true, 2513 'staticaccelerationsize' => 3, 2514 )); 2515 $instance->phpunit_add_definition('phpunit/accelerated3', array( 2516 'mode' => cache_store::MODE_APPLICATION, 2517 'component' => 'phpunit', 2518 'area' => 'accelerated3', 2519 'staticacceleration' => true, 2520 'staticaccelerationsize' => 3, 2521 )); 2522 $instance->phpunit_add_definition('phpunit/accelerated4', array( 2523 'mode' => cache_store::MODE_APPLICATION, 2524 'component' => 'phpunit', 2525 'area' => 'accelerated4', 2526 'staticacceleration' => true, 2527 'staticaccelerationsize' => 4, 2528 )); 2529 $instance->phpunit_add_definition('phpunit/simpledataarea1', array( 2530 'mode' => cache_store::MODE_APPLICATION, 2531 'component' => 'phpunit', 2532 'area' => 'simpledataarea1', 2533 'staticacceleration' => true, 2534 'simpledata' => false 2535 )); 2536 $instance->phpunit_add_definition('phpunit/simpledataarea2', array( 2537 'mode' => cache_store::MODE_APPLICATION, 2538 'component' => 'phpunit', 2539 'area' => 'simpledataarea2', 2540 'staticacceleration' => true, 2541 'simpledata' => true 2542 )); 2543 2544 $cache = cache::make('phpunit', 'accelerated'); 2545 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 2546 2547 // Set and get three elements. 2548 $this->assertTrue($cache->set('a', 'A')); 2549 $this->assertTrue($cache->set('b', 'B')); 2550 $this->assertTrue($cache->set('c', 'C')); 2551 $this->assertEquals('A', $cache->get('a')); 2552 $this->assertEquals(array('b' => 'B', 'c' => 'C'), $cache->get_many(array('b', 'c'))); 2553 2554 // Make sure all items are in static acceleration array. 2555 $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); 2556 $this->assertEquals('B', $cache->phpunit_static_acceleration_get('b')); 2557 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2558 2559 // Add new value and make sure it is in cache and it is in array. 2560 $this->assertTrue($cache->set('d', 'D')); 2561 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2562 $this->assertEquals('D', $cache->get('d')); 2563 2564 // Now the least recent accessed item (a) is no longer in acceleration array. 2565 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2566 $this->assertEquals('B', $cache->phpunit_static_acceleration_get('b')); 2567 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2568 2569 // Adding and deleting element. 2570 $this->assertTrue($cache->set('a', 'A')); 2571 $this->assertTrue($cache->delete('a')); 2572 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2573 $this->assertFalse($cache->has('a')); 2574 2575 // Make sure "purge" deletes from the array as well. 2576 $cache->purge(); 2577 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2578 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2579 $this->assertFalse($cache->phpunit_static_acceleration_get('c')); 2580 $this->assertFalse($cache->phpunit_static_acceleration_get('d')); 2581 $this->assertFalse($cache->phpunit_static_acceleration_get('e')); 2582 2583 // Check that the array holds the last accessed items by get/set. 2584 $this->assertTrue($cache->set('a', 'A')); 2585 $this->assertTrue($cache->set('b', 'B')); 2586 $this->assertTrue($cache->set('c', 'C')); 2587 $this->assertTrue($cache->set('d', 'D')); 2588 $this->assertTrue($cache->set('e', 'E')); 2589 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2590 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2591 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2592 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2593 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2594 2595 // Store a cacheable_object, get many times and ensure each time wake_for_cache is used. 2596 // Both get and get_many are tested. Two cache entries are used to ensure the times aren't 2597 // confused with multiple calls to get()/get_many(). 2598 $startmicrotime = microtime(true); 2599 $cacheableobject = new cache_phpunit_dummy_object(1, 1, $startmicrotime); 2600 $cacheableobject2 = new cache_phpunit_dummy_object(2, 2, $startmicrotime); 2601 $this->assertTrue($cache->set('a', $cacheableobject)); 2602 $this->assertTrue($cache->set('b', $cacheableobject2)); 2603 $staticaccelerationreturntime = $cache->phpunit_static_acceleration_get('a')->propertytime; 2604 $staticaccelerationreturntimeb = $cache->phpunit_static_acceleration_get('b')->propertytime; 2605 $this->assertGreaterThanOrEqual($startmicrotime, $staticaccelerationreturntime, 'Restore time of static must be newer.'); 2606 2607 // Reset the static cache without resetting backing store. 2608 $cache->phpunit_static_acceleration_purge(); 2609 2610 // Get the value from the backend store, populating the static cache. 2611 $cachevalue = $cache->get('a'); 2612 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $cachevalue); 2613 $this->assertGreaterThanOrEqual($staticaccelerationreturntime, $cachevalue->propertytime); 2614 $backingstorereturntime = $cachevalue->propertytime; 2615 2616 $results = $cache->get_many(array('b')); 2617 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $results['b']); 2618 $this->assertGreaterThanOrEqual($staticaccelerationreturntimeb, $results['b']->propertytime); 2619 $backingstorereturntimeb = $results['b']->propertytime; 2620 2621 // Obtain the value again and confirm that static cache is using wake_from_cache. 2622 // Upon failure, the times are not adjusted as wake_from_cache is skipped as the 2623 // value is stored serialized in the static acceleration cache. 2624 $cachevalue = $cache->phpunit_static_acceleration_get('a'); 2625 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $cachevalue); 2626 $this->assertGreaterThanOrEqual($backingstorereturntime, $cachevalue->propertytime); 2627 2628 $results = $cache->get_many(array('b')); 2629 $this->assertInstanceOf(cache_phpunit_dummy_object::class, $results['b']); 2630 $this->assertGreaterThanOrEqual($backingstorereturntimeb, $results['b']->propertytime); 2631 2632 /** @var cache_phpunit_application $cache */ 2633 $cache = cache::make('phpunit', 'accelerated2'); 2634 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 2635 2636 // Check that the array holds the last accessed items by get/set. 2637 $this->assertTrue($cache->set('a', 'A')); 2638 $this->assertTrue($cache->set('b', 'B')); 2639 $this->assertTrue($cache->set('c', 'C')); 2640 $this->assertTrue($cache->set('d', 'D')); 2641 $this->assertTrue($cache->set('e', 'E')); 2642 // Current keys in the array: c, d, e. 2643 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2644 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2645 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2646 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2647 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2648 2649 $this->assertEquals('A', $cache->get('a')); 2650 // Current keys in the array: d, e, a. 2651 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2652 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2653 $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); 2654 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2655 $this->assertFalse($cache->phpunit_static_acceleration_get('c')); 2656 2657 // Current keys in the array: d, e, a. 2658 $this->assertEquals(array('c' => 'C'), $cache->get_many(array('c'))); 2659 // Current keys in the array: e, a, c. 2660 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2661 $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); 2662 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2663 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2664 $this->assertFalse($cache->phpunit_static_acceleration_get('d')); 2665 2666 2667 $cache = cache::make('phpunit', 'accelerated3'); 2668 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 2669 2670 // Check that the array holds the last accessed items by get/set. 2671 $this->assertTrue($cache->set('a', 'A')); 2672 $this->assertTrue($cache->set('b', 'B')); 2673 $this->assertTrue($cache->set('c', 'C')); 2674 $this->assertTrue($cache->set('d', 'D')); 2675 $this->assertTrue($cache->set('e', 'E')); 2676 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2677 $this->assertFalse($cache->phpunit_static_acceleration_get('b')); 2678 $this->assertEquals('C', $cache->phpunit_static_acceleration_get('c')); 2679 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2680 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2681 2682 $this->assertTrue($cache->set('b', 'B2')); 2683 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2684 $this->assertEquals('B2', $cache->phpunit_static_acceleration_get('b')); 2685 $this->assertFalse($cache->phpunit_static_acceleration_get('c')); 2686 $this->assertEquals('D', $cache->phpunit_static_acceleration_get('d')); 2687 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2688 2689 $this->assertEquals(2, $cache->set_many(array('b' => 'B3', 'c' => 'C3'))); 2690 $this->assertFalse($cache->phpunit_static_acceleration_get('a')); 2691 $this->assertEquals('B3', $cache->phpunit_static_acceleration_get('b')); 2692 $this->assertEquals('C3', $cache->phpunit_static_acceleration_get('c')); 2693 $this->assertFalse($cache->phpunit_static_acceleration_get('d')); 2694 $this->assertEquals('E', $cache->phpunit_static_acceleration_get('e')); 2695 2696 $cache = cache::make('phpunit', 'accelerated4'); 2697 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 2698 $this->assertTrue($cache->set('a', 'A')); 2699 $this->assertTrue($cache->set('a', 'A')); 2700 $this->assertTrue($cache->set('a', 'A')); 2701 $this->assertTrue($cache->set('a', 'A')); 2702 $this->assertTrue($cache->set('a', 'A')); 2703 $this->assertTrue($cache->set('a', 'A')); 2704 $this->assertTrue($cache->set('a', 'A')); 2705 $this->assertEquals('A', $cache->phpunit_static_acceleration_get('a')); 2706 $this->assertEquals('A', $cache->get('a')); 2707 2708 // Setting simpledata to false objects are cloned when retrieving data. 2709 $cache = cache::make('phpunit', 'simpledataarea1'); 2710 $notreallysimple = new \stdClass(); 2711 $notreallysimple->name = 'a'; 2712 $cache->set('a', $notreallysimple); 2713 $returnedinstance1 = $cache->get('a'); 2714 $returnedinstance2 = $cache->get('a'); 2715 $returnedinstance1->name = 'b'; 2716 $this->assertEquals('a', $returnedinstance2->name); 2717 2718 // Setting simpledata to true we assume that data does not contain references. 2719 $cache = cache::make('phpunit', 'simpledataarea2'); 2720 $notreallysimple = new \stdClass(); 2721 $notreallysimple->name = 'a'; 2722 $cache->set('a', $notreallysimple); 2723 $returnedinstance1 = $cache->get('a'); 2724 $returnedinstance2 = $cache->get('a'); 2725 $returnedinstance1->name = 'b'; 2726 $this->assertEquals('b', $returnedinstance2->name); 2727 } 2728 2729 public function test_identifiers_have_separate_caches() { 2730 $cachepg = cache::make('core', 'databasemeta', array('dbfamily' => 'pgsql')); 2731 $cachepg->set(1, 'here'); 2732 $cachemy = cache::make('core', 'databasemeta', array('dbfamily' => 'mysql')); 2733 $cachemy->set(2, 'there'); 2734 $this->assertEquals('here', $cachepg->get(1)); 2735 $this->assertEquals('there', $cachemy->get(2)); 2736 $this->assertFalse($cachemy->get(1)); 2737 } 2738 2739 public function test_performance_debug() { 2740 global $CFG; 2741 $this->resetAfterTest(true); 2742 $CFG->perfdebug = 15; 2743 2744 $instance = cache_config_testing::instance(); 2745 $applicationid = 'phpunit/applicationperf'; 2746 $instance->phpunit_add_definition($applicationid, array( 2747 'mode' => cache_store::MODE_APPLICATION, 2748 'component' => 'phpunit', 2749 'area' => 'applicationperf' 2750 )); 2751 $sessionid = 'phpunit/sessionperf'; 2752 $instance->phpunit_add_definition($sessionid, array( 2753 'mode' => cache_store::MODE_SESSION, 2754 'component' => 'phpunit', 2755 'area' => 'sessionperf' 2756 )); 2757 $requestid = 'phpunit/requestperf'; 2758 $instance->phpunit_add_definition($requestid, array( 2759 'mode' => cache_store::MODE_REQUEST, 2760 'component' => 'phpunit', 2761 'area' => 'requestperf' 2762 )); 2763 2764 $application = cache::make('phpunit', 'applicationperf'); 2765 $session = cache::make('phpunit', 'sessionperf'); 2766 $request = cache::make('phpunit', 'requestperf'); 2767 2768 // Check that no stats are recorded for these definitions yet. 2769 $stats = cache_helper::get_stats(); 2770 $this->assertArrayNotHasKey($applicationid, $stats); 2771 $this->assertArrayHasKey($sessionid, $stats); // Session cache sets a key on construct. 2772 $this->assertArrayNotHasKey($requestid, $stats); 2773 2774 // Check that stores register misses. 2775 $this->assertFalse($application->get('missMe')); 2776 $this->assertFalse($application->get('missMe')); 2777 $this->assertFalse($session->get('missMe')); 2778 $this->assertFalse($session->get('missMe')); 2779 $this->assertFalse($session->get('missMe')); 2780 $this->assertFalse($request->get('missMe')); 2781 $this->assertFalse($request->get('missMe')); 2782 $this->assertFalse($request->get('missMe')); 2783 $this->assertFalse($request->get('missMe')); 2784 2785 $endstats = cache_helper::get_stats(); 2786 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['misses']); 2787 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits']); 2788 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets']); 2789 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['misses']); 2790 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits']); 2791 $this->assertEquals(1, $endstats[$sessionid]['stores']['default_session']['sets']); 2792 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['misses']); 2793 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits']); 2794 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets']); 2795 2796 $startstats = cache_helper::get_stats(); 2797 2798 // Check that stores register sets. 2799 $this->assertTrue($application->set('setMe1', 1)); 2800 $this->assertTrue($application->set('setMe2', 2)); 2801 $this->assertTrue($session->set('setMe1', 1)); 2802 $this->assertTrue($session->set('setMe2', 2)); 2803 $this->assertTrue($session->set('setMe3', 3)); 2804 $this->assertTrue($request->set('setMe1', 1)); 2805 $this->assertTrue($request->set('setMe2', 2)); 2806 $this->assertTrue($request->set('setMe3', 3)); 2807 $this->assertTrue($request->set('setMe4', 4)); 2808 2809 $endstats = cache_helper::get_stats(); 2810 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - 2811 $startstats[$applicationid]['stores']['default_application']['misses']); 2812 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] - 2813 $startstats[$applicationid]['stores']['default_application']['hits']); 2814 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] - 2815 $startstats[$applicationid]['stores']['default_application']['sets']); 2816 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - 2817 $startstats[$sessionid]['stores']['default_session']['misses']); 2818 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] - 2819 $startstats[$sessionid]['stores']['default_session']['hits']); 2820 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] - 2821 $startstats[$sessionid]['stores']['default_session']['sets']); 2822 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - 2823 $startstats[$requestid]['stores']['default_request']['misses']); 2824 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] - 2825 $startstats[$requestid]['stores']['default_request']['hits']); 2826 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] - 2827 $startstats[$requestid]['stores']['default_request']['sets']); 2828 2829 $startstats = cache_helper::get_stats(); 2830 2831 // Check that stores register hits. 2832 $this->assertEquals($application->get('setMe1'), 1); 2833 $this->assertEquals($application->get('setMe2'), 2); 2834 $this->assertEquals($session->get('setMe1'), 1); 2835 $this->assertEquals($session->get('setMe2'), 2); 2836 $this->assertEquals($session->get('setMe3'), 3); 2837 $this->assertEquals($request->get('setMe1'), 1); 2838 $this->assertEquals($request->get('setMe2'), 2); 2839 $this->assertEquals($request->get('setMe3'), 3); 2840 $this->assertEquals($request->get('setMe4'), 4); 2841 2842 $endstats = cache_helper::get_stats(); 2843 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - 2844 $startstats[$applicationid]['stores']['default_application']['misses']); 2845 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] - 2846 $startstats[$applicationid]['stores']['default_application']['hits']); 2847 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] - 2848 $startstats[$applicationid]['stores']['default_application']['sets']); 2849 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - 2850 $startstats[$sessionid]['stores']['default_session']['misses']); 2851 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] - 2852 $startstats[$sessionid]['stores']['default_session']['hits']); 2853 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] - 2854 $startstats[$sessionid]['stores']['default_session']['sets']); 2855 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - 2856 $startstats[$requestid]['stores']['default_request']['misses']); 2857 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] - 2858 $startstats[$requestid]['stores']['default_request']['hits']); 2859 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] - 2860 $startstats[$requestid]['stores']['default_request']['sets']); 2861 2862 $startstats = cache_helper::get_stats(); 2863 2864 // Check that stores register through get_many. 2865 $application->get_many(array('setMe1', 'setMe2')); 2866 $session->get_many(array('setMe1', 'setMe2', 'setMe3')); 2867 $request->get_many(array('setMe1', 'setMe2', 'setMe3', 'setMe4')); 2868 2869 $endstats = cache_helper::get_stats(); 2870 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - 2871 $startstats[$applicationid]['stores']['default_application']['misses']); 2872 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['hits'] - 2873 $startstats[$applicationid]['stores']['default_application']['hits']); 2874 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['sets'] - 2875 $startstats[$applicationid]['stores']['default_application']['sets']); 2876 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - 2877 $startstats[$sessionid]['stores']['default_session']['misses']); 2878 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['hits'] - 2879 $startstats[$sessionid]['stores']['default_session']['hits']); 2880 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['sets'] - 2881 $startstats[$sessionid]['stores']['default_session']['sets']); 2882 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - 2883 $startstats[$requestid]['stores']['default_request']['misses']); 2884 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['hits'] - 2885 $startstats[$requestid]['stores']['default_request']['hits']); 2886 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['sets'] - 2887 $startstats[$requestid]['stores']['default_request']['sets']); 2888 2889 $startstats = cache_helper::get_stats(); 2890 2891 // Check that stores register through set_many. 2892 $this->assertEquals(2, $application->set_many(['setMe1' => 1, 'setMe2' => 2])); 2893 $this->assertEquals(3, $session->set_many(['setMe1' => 1, 'setMe2' => 2, 'setMe3' => 3])); 2894 $this->assertEquals(4, $request->set_many(['setMe1' => 1, 'setMe2' => 2, 'setMe3' => 3, 'setMe4' => 4])); 2895 2896 $endstats = cache_helper::get_stats(); 2897 2898 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['misses'] - 2899 $startstats[$applicationid]['stores']['default_application']['misses']); 2900 $this->assertEquals(0, $endstats[$applicationid]['stores']['default_application']['hits'] - 2901 $startstats[$applicationid]['stores']['default_application']['hits']); 2902 $this->assertEquals(2, $endstats[$applicationid]['stores']['default_application']['sets'] - 2903 $startstats[$applicationid]['stores']['default_application']['sets']); 2904 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['misses'] - 2905 $startstats[$sessionid]['stores']['default_session']['misses']); 2906 $this->assertEquals(0, $endstats[$sessionid]['stores']['default_session']['hits'] - 2907 $startstats[$sessionid]['stores']['default_session']['hits']); 2908 $this->assertEquals(3, $endstats[$sessionid]['stores']['default_session']['sets'] - 2909 $startstats[$sessionid]['stores']['default_session']['sets']); 2910 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['misses'] - 2911 $startstats[$requestid]['stores']['default_request']['misses']); 2912 $this->assertEquals(0, $endstats[$requestid]['stores']['default_request']['hits'] - 2913 $startstats[$requestid]['stores']['default_request']['hits']); 2914 $this->assertEquals(4, $endstats[$requestid]['stores']['default_request']['sets'] - 2915 $startstats[$requestid]['stores']['default_request']['sets']); 2916 } 2917 2918 /** 2919 * Data provider for static acceleration performance tests. 2920 * 2921 * @return array 2922 */ 2923 public function static_acceleration_performance_provider(): array { 2924 // Note: These are the delta values, not the absolute values. 2925 // Also note that the set will actually store the valuein the static cache immediately. 2926 $validfirst = [ 2927 'default_application' => [ 2928 'hits' => 1, 2929 'misses' => 0, 2930 ], 2931 cache_store::STATIC_ACCEL => [ 2932 'hits' => 0, 2933 'misses' => 1, 2934 ], 2935 ]; 2936 2937 $validsecond = [ 2938 'default_application' => [ 2939 'hits' => 0, 2940 'misses' => 0, 2941 ], 2942 cache_store::STATIC_ACCEL => [ 2943 'hits' => 1, 2944 'misses' => 0, 2945 ], 2946 ]; 2947 2948 $invalidfirst = [ 2949 'default_application' => [ 2950 'hits' => 0, 2951 'misses' => 1, 2952 ], 2953 cache_store::STATIC_ACCEL => [ 2954 'hits' => 0, 2955 'misses' => 1, 2956 ], 2957 ]; 2958 $invalidsecond = [ 2959 'default_application' => [ 2960 'hits' => 0, 2961 'misses' => 1, 2962 ], 2963 cache_store::STATIC_ACCEL => [ 2964 'hits' => 0, 2965 'misses' => 1, 2966 ], 2967 ];; 2968 2969 return [ 2970 'Truthy' => [ 2971 true, 2972 $validfirst, 2973 $validsecond, 2974 ], 2975 'Null' => [ 2976 null, 2977 $validfirst, 2978 $validsecond, 2979 ], 2980 'Empty Array' => [ 2981 [], 2982 $validfirst, 2983 $validsecond, 2984 ], 2985 'Empty String' => [ 2986 '', 2987 $validfirst, 2988 $validsecond, 2989 ], 2990 'False' => [ 2991 false, 2992 $invalidfirst, 2993 $invalidsecond, 2994 ], 2995 ]; 2996 } 2997 2998 /** 2999 * Test performance of static acceleration caches with values which are frequently confused with missing values. 3000 * 3001 * @dataProvider static_acceleration_performance_provider 3002 * @param mixed $value The value to test 3003 * @param array $firstfetchstats The expected stats on the first fetch 3004 * @param array $secondfetchstats The expected stats on the subsequent fetch 3005 */ 3006 public function test_static_acceleration_values_performance( 3007 $value, 3008 array $firstfetchstats, 3009 array $secondfetchstats 3010 ): void { 3011 // Note: We need to modify perfdebug to test this. 3012 global $CFG; 3013 $this->resetAfterTest(true); 3014 $CFG->perfdebug = 15; 3015 3016 $instance = cache_config_testing::instance(); 3017 $instance->phpunit_add_definition('phpunit/accelerated', [ 3018 'mode' => cache_store::MODE_APPLICATION, 3019 'component' => 'phpunit', 3020 'area' => 'accelerated', 3021 'staticacceleration' => true, 3022 'staticaccelerationsize' => 1, 3023 ]); 3024 3025 $cache = cache::make('phpunit', 'accelerated'); 3026 $this->assertInstanceOf(cache_phpunit_application::class, $cache); 3027 3028 $this->assertTrue($cache->set('value', $value)); 3029 3030 $checkstats = function( 3031 array $start, 3032 array $expectedstats 3033 ): array { 3034 $applicationid = 'phpunit/accelerated'; 3035 $endstats = cache_helper::get_stats(); 3036 3037 $start = $start[$applicationid]['stores']; 3038 $end = $endstats[$applicationid]['stores']; 3039 3040 foreach ($expectedstats as $cachename => $expected) { 3041 foreach ($expected as $type => $value) { 3042 $startvalue = array_key_exists($cachename, $start) ? $start[$cachename][$type] : 0; 3043 $endvalue = array_key_exists($cachename, $end) ? $end[$cachename][$type] : 0; 3044 $diff = $endvalue - $startvalue; 3045 $this->assertEquals( 3046 $value, 3047 $diff, 3048 "Expected $cachename $type to be $value, got $diff" 3049 ); 3050 } 3051 } 3052 3053 return $endstats; 3054 }; 3055 3056 // Reset the cache factory so that we can get the stats from a fresh instance. 3057 $factory = cache_factory::instance(); 3058 $factory->reset_cache_instances(); 3059 $cache = cache::make('phpunit', 'accelerated'); 3060 3061 // Get the initial stats. 3062 $startstats = cache_helper::get_stats(); 3063 3064 // Fetching the value the first time should seed the static cache from the application cache. 3065 $this->assertEquals($value, $cache->get('value')); 3066 $startstats = $checkstats($startstats, $firstfetchstats); 3067 3068 // Fetching the value should only hit the static cache. 3069 $this->assertEquals($value, $cache->get('value')); 3070 $checkstats($startstats, $secondfetchstats); 3071 } 3072 3073 3074 public function test_static_cache() { 3075 global $CFG; 3076 $this->resetAfterTest(true); 3077 $CFG->perfdebug = 15; 3078 3079 // Create cache store with static acceleration. 3080 $instance = cache_config_testing::instance(); 3081 $applicationid = 'phpunit/applicationperf'; 3082 $instance->phpunit_add_definition($applicationid, array( 3083 'mode' => cache_store::MODE_APPLICATION, 3084 'component' => 'phpunit', 3085 'area' => 'applicationperf', 3086 'simplekeys' => true, 3087 'staticacceleration' => true, 3088 'staticaccelerationsize' => 3 3089 )); 3090 3091 $application = cache::make('phpunit', 'applicationperf'); 3092 3093 // Check that stores register sets. 3094 $this->assertTrue($application->set('setMe1', 1)); 3095 $this->assertTrue($application->set('setMe2', 0)); 3096 $this->assertTrue($application->set('setMe3', array())); 3097 $this->assertTrue($application->get('setMe1') !== false); 3098 $this->assertTrue($application->get('setMe2') !== false); 3099 $this->assertTrue($application->get('setMe3') !== false); 3100 3101 // Check that the static acceleration worked, even on empty arrays and the number 0. 3102 $endstats = cache_helper::get_stats(); 3103 $this->assertEquals(0, $endstats[$applicationid]['stores']['** static accel. **']['misses']); 3104 $this->assertEquals(3, $endstats[$applicationid]['stores']['** static accel. **']['hits']); 3105 } 3106 3107 public function test_performance_debug_off() { 3108 global $CFG; 3109 $this->resetAfterTest(true); 3110 $CFG->perfdebug = 7; 3111 3112 $instance = cache_config_testing::instance(); 3113 $applicationid = 'phpunit/applicationperfoff'; 3114 $instance->phpunit_add_definition($applicationid, array( 3115 'mode' => cache_store::MODE_APPLICATION, 3116 'component' => 'phpunit', 3117 'area' => 'applicationperfoff' 3118 )); 3119 $sessionid = 'phpunit/sessionperfoff'; 3120 $instance->phpunit_add_definition($sessionid, array( 3121 'mode' => cache_store::MODE_SESSION, 3122 'component' => 'phpunit', 3123 'area' => 'sessionperfoff' 3124 )); 3125 $requestid = 'phpunit/requestperfoff'; 3126 $instance->phpunit_add_definition($requestid, array( 3127 'mode' => cache_store::MODE_REQUEST, 3128 'component' => 'phpunit', 3129 'area' => 'requestperfoff' 3130 )); 3131 3132 $application = cache::make('phpunit', 'applicationperfoff'); 3133 $session = cache::make('phpunit', 'sessionperfoff'); 3134 $request = cache::make('phpunit', 'requestperfoff'); 3135 3136 // Check that no stats are recorded for these definitions yet. 3137 $stats = cache_helper::get_stats(); 3138 $this->assertArrayNotHasKey($applicationid, $stats); 3139 $this->assertArrayNotHasKey($sessionid, $stats); 3140 $this->assertArrayNotHasKey($requestid, $stats); 3141 3142 // Trigger cache misses, cache sets and cache hits. 3143 $this->assertFalse($application->get('missMe')); 3144 $this->assertTrue($application->set('setMe', 1)); 3145 $this->assertEquals(1, $application->get('setMe')); 3146 $this->assertFalse($session->get('missMe')); 3147 $this->assertTrue($session->set('setMe', 3)); 3148 $this->assertEquals(3, $session->get('setMe')); 3149 $this->assertFalse($request->get('missMe')); 3150 $this->assertTrue($request->set('setMe', 4)); 3151 $this->assertEquals(4, $request->get('setMe')); 3152 3153 // Check that no stats are being recorded for these definitions. 3154 $endstats = cache_helper::get_stats(); 3155 $this->assertArrayNotHasKey($applicationid, $endstats); 3156 $this->assertArrayNotHasKey($sessionid, $endstats); 3157 $this->assertArrayNotHasKey($requestid, $endstats); 3158 } 3159 3160 /** 3161 * Tests session cache event purge and subsequent visit in the same request. 3162 * 3163 * This test simulates a cache being created, a value being set, then the value being purged. 3164 * A subsequent use of the same cache is started in the same request which fills the cache. 3165 * A new request is started a short time later. 3166 * The cache should be filled. 3167 */ 3168 public function test_session_event_purge_same_second() { 3169 $instance = cache_config_testing::instance(); 3170 $instance->phpunit_add_definition('phpunit/eventpurgetest', array( 3171 'mode' => cache_store::MODE_SESSION, 3172 'component' => 'phpunit', 3173 'area' => 'eventpurgetest', 3174 'invalidationevents' => array( 3175 'crazyevent', 3176 ) 3177 )); 3178 3179 // Create the cache, set a value, and immediately purge it by event. 3180 $cache = cache::make('phpunit', 'eventpurgetest'); 3181 $cache->set('testkey1', 'test data 1'); 3182 $this->assertEquals('test data 1', $cache->get('testkey1')); 3183 cache_helper::purge_by_event('crazyevent'); 3184 $this->assertFalse($cache->get('testkey1')); 3185 3186 // Set up the cache again in the same request and add a new value back in. 3187 $factory = cache_factory::instance(); 3188 $factory->reset_cache_instances(); 3189 $cache = cache::make('phpunit', 'eventpurgetest'); 3190 $cache->set('testkey1', 'test data 2'); 3191 $this->assertEquals('test data 2', $cache->get('testkey1')); 3192 3193 // Trick the cache into thinking that this is a new request. 3194 cache_phpunit_cache::simulate_new_request(); 3195 $factory = cache_factory::instance(); 3196 $factory->reset_cache_instances(); 3197 3198 // Set up the cache again. 3199 // This is a subsequent request at a new time, so we instead the invalidation time will be checked. 3200 // The invalidation time should match the last purged time and the cache will not be re-purged. 3201 $cache = cache::make('phpunit', 'eventpurgetest'); 3202 $this->assertEquals('test data 2', $cache->get('testkey1')); 3203 } 3204 3205 /** 3206 * Test that values set in different sessions are stored with different key prefixes. 3207 */ 3208 public function test_session_distinct_storage_key() { 3209 $this->resetAfterTest(); 3210 3211 // Prepare a dummy session cache configuration. 3212 $config = cache_config_testing::instance(); 3213 $config->phpunit_add_definition('phpunit/test_session_distinct_storage_key', array( 3214 'mode' => cache_store::MODE_SESSION, 3215 'component' => 'phpunit', 3216 'area' => 'test_session_distinct_storage_key' 3217 )); 3218 3219 // First anonymous user's session cache. 3220 cache_phpunit_session::phpunit_mockup_session_id('foo'); 3221 $this->setUser(0); 3222 $cache1 = cache::make('phpunit', 'test_session_distinct_storage_key'); 3223 3224 // Reset cache instances to emulate a new request. 3225 cache_factory::instance()->reset_cache_instances(); 3226 3227 // Another anonymous user's session cache. 3228 cache_phpunit_session::phpunit_mockup_session_id('bar'); 3229 $this->setUser(0); 3230 $cache2 = cache::make('phpunit', 'test_session_distinct_storage_key'); 3231 3232 cache_factory::instance()->reset_cache_instances(); 3233 3234 // Guest user's session cache. 3235 cache_phpunit_session::phpunit_mockup_session_id('baz'); 3236 $this->setGuestUser(); 3237 $cache3 = cache::make('phpunit', 'test_session_distinct_storage_key'); 3238 3239 cache_factory::instance()->reset_cache_instances(); 3240 3241 // Same guest user's session cache but in another browser window. 3242 cache_phpunit_session::phpunit_mockup_session_id('baz'); 3243 $this->setGuestUser(); 3244 $cache4 = cache::make('phpunit', 'test_session_distinct_storage_key'); 3245 3246 // Assert that different PHP session implies different key prefix for storing values. 3247 $this->assertNotEquals($cache1->phpunit_get_key_prefix(), $cache2->phpunit_get_key_prefix()); 3248 3249 // Assert that same PHP session implies same key prefix for storing values. 3250 $this->assertEquals($cache3->phpunit_get_key_prefix(), $cache4->phpunit_get_key_prefix()); 3251 } 3252 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body