Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Support library for the cache PHPUnit tests. 19 * 20 * This file is part of Moodle's cache API, affectionately called MUC. 21 * It contains the components that are requried in order to use caching. 22 * 23 * @package core 24 * @category cache 25 * @copyright 2012 Sam Hemelryk 26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 */ 28 29 defined('MOODLE_INTERNAL') || die(); 30 31 require_once($CFG->dirroot.'/cache/locallib.php'); 32 33 /** 34 * Override the default cache configuration for our own maniacal purposes. 35 * 36 * This class was originally named cache_config_phpunittest but was renamed in 2.9 37 * because it is used for both unit tests and acceptance tests. 38 * 39 * @since 2.9 40 * @copyright 2012 Sam Hemelryk 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class cache_config_testing extends cache_config_writer { 44 45 /** 46 * Creates the default configuration and saves it. 47 * 48 * This function calls config_save, however it is safe to continue using it afterwards as this function should only ever 49 * be called when there is no configuration file already. 50 * 51 * @param bool $forcesave If set to true then we will forcefully save the default configuration file. 52 * @return true|array Returns true if the default configuration was successfully created. 53 * Returns a configuration array if it could not be saved. This is a bad situation. Check your error logs. 54 */ 55 public static function create_default_configuration($forcesave = false) { 56 global $CFG; 57 // HACK ALERT. 58 // We probably need to come up with a better way to create the default stores, or at least ensure 100% that the 59 // default store plugins are protected from deletion. 60 $writer = new self; 61 $writer->configstores = self::get_default_stores(); 62 $writer->configdefinitions = self::locate_definitions(); 63 $defaultapplication = 'default_application'; 64 65 $appdefine = defined('TEST_CACHE_USING_APPLICATION_STORE') ? TEST_CACHE_USING_APPLICATION_STORE : false; 66 if ($appdefine !== false && preg_match('/^[a-zA-Z][a-zA-Z0-9_]+$/', $appdefine)) { 67 $expectedstore = $appdefine; 68 $file = $CFG->dirroot.'/cache/stores/'.$appdefine.'/lib.php'; 69 $class = 'cachestore_'.$appdefine; 70 if (file_exists($file)) { 71 require_once($file); 72 } 73 if (class_exists($class) && $class::ready_to_be_used_for_testing()) { 74 /* @var cache_store $class */ 75 $writer->configstores['test_application'] = array( 76 'name' => 'test_application', 77 'plugin' => $expectedstore, 78 'modes' => $class::get_supported_modes(), 79 'features' => $class::get_supported_features(), 80 'configuration' => $class::unit_test_configuration() 81 ); 82 83 $defaultapplication = 'test_application'; 84 } 85 } 86 87 $writer->configmodemappings = array( 88 array( 89 'mode' => cache_store::MODE_APPLICATION, 90 'store' => $defaultapplication, 91 'sort' => -1 92 ), 93 array( 94 'mode' => cache_store::MODE_SESSION, 95 'store' => 'default_session', 96 'sort' => -1 97 ), 98 array( 99 'mode' => cache_store::MODE_REQUEST, 100 'store' => 'default_request', 101 'sort' => -1 102 ) 103 ); 104 $writer->configlocks = array( 105 'default_file_lock' => array( 106 'name' => 'cachelock_file_default', 107 'type' => 'cachelock_file', 108 'dir' => 'filelocks', 109 'default' => true 110 ) 111 ); 112 113 $factory = cache_factory::instance(); 114 // We expect the cache to be initialising presently. If its not then something has gone wrong and likely 115 // we are now in a loop. 116 if (!$forcesave && $factory->get_state() !== cache_factory::STATE_INITIALISING) { 117 return $writer->generate_configuration_array(); 118 } 119 $factory->set_state(cache_factory::STATE_SAVING); 120 $writer->config_save(); 121 return true; 122 } 123 124 /** 125 * Returns the expected path to the configuration file. 126 * 127 * We override this function to add handling for $CFG->altcacheconfigpath. 128 * We want to support it so that people can run unit tests against alternative cache setups. 129 * However we don't want to ever make changes to the file at $CFG->altcacheconfigpath so we 130 * always use dataroot and copy the alt file there as required. 131 * 132 * @throws cache_exception 133 * @return string The absolute path 134 */ 135 protected static function get_config_file_path() { 136 global $CFG; 137 // We always use this path. 138 $configpath = $CFG->dataroot.'/muc/config.php'; 139 140 if (!empty($CFG->altcacheconfigpath)) { 141 142 // No need to check we are within a test here, this is the cache config class that gets used 143 // only when one of those is true. 144 if (!defined('TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH') || !TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH) { 145 // TEST_CACHE_USING_ALT_CACHE_CONFIG_PATH has not being defined or is false, we want to use the default. 146 return $configpath; 147 } 148 149 $path = $CFG->altcacheconfigpath; 150 if (is_dir($path) && is_writable($path)) { 151 // Its a writable directory, thats fine. Convert it to a file. 152 $path = $CFG->altcacheconfigpath.'/cacheconfig.php'; 153 } 154 if (is_readable($path)) { 155 $directory = dirname($configpath); 156 if ($directory !== $CFG->dataroot && !file_exists($directory)) { 157 $result = make_writable_directory($directory, false); 158 if (!$result) { 159 throw new cache_exception('ex_configcannotsave', 'cache', '', null, 'Cannot create config directory. Check the permissions on your moodledata directory.'); 160 } 161 } 162 // We don't care that this fails but we should let the developer know. 163 if (!is_readable($configpath) && !@copy($path, $configpath)) { 164 debugging('Failed to copy alt cache config file to required location'); 165 } 166 } 167 } 168 169 // We always use the dataroot location. 170 return $configpath; 171 } 172 173 /** 174 * Adds a definition to the stack 175 * @param string $area 176 * @param array $properties 177 * @param bool $addmapping By default this method adds a definition and a mapping for that definition. You can 178 * however set this to false if you only want it to add the definition and not the mapping. 179 */ 180 public function phpunit_add_definition($area, array $properties, $addmapping = true) { 181 if (!array_key_exists('overrideclass', $properties)) { 182 switch ($properties['mode']) { 183 case cache_store::MODE_APPLICATION: 184 $properties['overrideclass'] = 'cache_phpunit_application'; 185 break; 186 case cache_store::MODE_SESSION: 187 $properties['overrideclass'] = 'cache_phpunit_session'; 188 break; 189 case cache_store::MODE_REQUEST: 190 $properties['overrideclass'] = 'cache_phpunit_request'; 191 break; 192 } 193 } 194 $this->configdefinitions[$area] = $properties; 195 if ($addmapping) { 196 switch ($properties['mode']) { 197 case cache_store::MODE_APPLICATION: 198 $this->phpunit_add_definition_mapping($area, 'default_application', 0); 199 break; 200 case cache_store::MODE_SESSION: 201 $this->phpunit_add_definition_mapping($area, 'default_session', 0); 202 break; 203 case cache_store::MODE_REQUEST: 204 $this->phpunit_add_definition_mapping($area, 'default_request', 0); 205 break; 206 } 207 } 208 } 209 210 /** 211 * Removes a definition. 212 * @param string $name 213 */ 214 public function phpunit_remove_definition($name) { 215 unset($this->configdefinitions[$name]); 216 } 217 218 /** 219 * Removes the configured stores so that there are none available. 220 */ 221 public function phpunit_remove_stores() { 222 $this->configstores = array(); 223 } 224 225 /** 226 * Forcefully adds a file store. 227 * 228 * You can turn off native TTL support if you want a way to test TTL wrapper objects. 229 * 230 * @param string $name 231 * @param bool $nativettl If false, uses fixture that turns off native TTL support 232 */ 233 public function phpunit_add_file_store(string $name, bool $nativettl = true): void { 234 if (!$nativettl) { 235 require_once (__DIR__ . '/cachestore_file_with_ttl_wrappers.php'); 236 } 237 $this->configstores[$name] = array( 238 'name' => $name, 239 'plugin' => 'file', 240 'configuration' => array( 241 'path' => '' 242 ), 243 'features' => 6, 244 'modes' => 3, 245 'mappingsonly' => false, 246 'class' => $nativettl ? 'cachestore_file' : 'cachestore_file_with_ttl_wrappers', 247 'default' => false, 248 'lock' => 'cachelock_file_default' 249 ); 250 } 251 252 /** 253 * Forcefully adds a session store. 254 * 255 * @param string $name 256 */ 257 public function phpunit_add_session_store($name) { 258 $this->configstores[$name] = array( 259 'name' => $name, 260 'plugin' => 'session', 261 'configuration' => array(), 262 'features' => 14, 263 'modes' => 2, 264 'default' => true, 265 'class' => 'cachestore_session', 266 'lock' => 'cachelock_file_default', 267 ); 268 } 269 270 /** 271 * Forcefully injects a definition => store mapping. 272 * 273 * This function does no validation, you should only be calling if it you know 274 * exactly what to expect. 275 * 276 * @param string $definition 277 * @param string $store 278 * @param int $sort 279 */ 280 public function phpunit_add_definition_mapping($definition, $store, $sort) { 281 $this->configdefinitionmappings[] = array( 282 'store' => $store, 283 'definition' => $definition, 284 'sort' => (int)$sort 285 ); 286 } 287 288 /** 289 * Overrides the default site identifier used by the Cache API so that we can be sure of what it is. 290 * 291 * @return string 292 */ 293 public function get_site_identifier() { 294 global $CFG; 295 return $CFG->wwwroot.'phpunit'; 296 } 297 298 /** 299 * Checks if the configuration file exists. 300 * 301 * @return bool True if it exists 302 */ 303 public static function config_file_exists() { 304 // Allow for late static binding by using static. 305 $configfilepath = static::get_config_file_path(); 306 307 // Invalidate opcode php cache, so we get correct status of file. 308 core_component::invalidate_opcode_php_cache($configfilepath); 309 return file_exists($configfilepath); 310 } 311 } 312 313 314 /** 315 * Dummy object for testing cacheable object interface and interaction 316 * 317 * Wake from cache needs specific testing at times to ensure that during multiple 318 * cache get() requests it's possible to verify that it's getting woken each time. 319 * 320 * @copyright 2012 Sam Hemelryk 321 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 322 */ 323 class cache_phpunit_dummy_object extends stdClass implements cacheable_object { 324 /** 325 * Test property 1 326 * @var string 327 */ 328 public $property1; 329 /** 330 * Test property 1 331 * @var string 332 */ 333 public $property2; 334 /** 335 * Test property time for verifying wake is run at each get() call. 336 * @var float 337 */ 338 public $propertytime; 339 /** 340 * Constructor 341 * @param string $property1 342 * @param string $property2 343 */ 344 public function __construct($property1, $property2, $propertytime = null) { 345 $this->property1 = $property1; 346 $this->property2 = $property2; 347 $this->propertytime = $propertytime === null ? microtime(true) : $propertytime; 348 } 349 /** 350 * Prepares this object for caching 351 * @return array 352 */ 353 public function prepare_to_cache() { 354 return array($this->property1.'_ptc', $this->property2.'_ptc', $this->propertytime); 355 } 356 /** 357 * Returns this object from the cache 358 * @param array $data 359 * @return cache_phpunit_dummy_object 360 */ 361 public static function wake_from_cache($data) { 362 $time = null; 363 if (!is_null($data[2])) { 364 // Windows 32bit microtime() resolution is 15ms, we ensure the time has moved forward. 365 do { 366 $time = microtime(true); 367 } while ($time == $data[2]); 368 369 } 370 return new cache_phpunit_dummy_object(array_shift($data).'_wfc', array_shift($data).'_wfc', $time); 371 } 372 } 373 374 /** 375 * Dummy data source object for testing data source interface and implementation 376 * 377 * @copyright 2012 Sam Hemelryk 378 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 379 */ 380 class cache_phpunit_dummy_datasource implements cache_data_source { 381 /** 382 * Returns an instance of this object for use with the cache. 383 * 384 * @param cache_definition $definition 385 * @return cache_phpunit_dummy_datasource 386 */ 387 public static function get_instance_for_cache(cache_definition $definition) { 388 return new cache_phpunit_dummy_datasource(); 389 } 390 391 /** 392 * Loads a key for the cache. 393 * 394 * @param string $key 395 * @return string 396 */ 397 public function load_for_cache($key) { 398 return $key.' has no value really.'; 399 } 400 401 /** 402 * Loads many keys for the cache 403 * 404 * @param array $keys 405 * @return array 406 */ 407 public function load_many_for_cache(array $keys) { 408 $return = array(); 409 foreach ($keys as $key) { 410 $return[$key] = $key.' has no value really.'; 411 } 412 return $return; 413 } 414 } 415 416 /** 417 * PHPUnit application cache loader. 418 * 419 * Used to expose things we could not otherwise see within an application cache. 420 * 421 * @copyright 2012 Sam Hemelryk 422 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 423 */ 424 class cache_phpunit_application extends cache_application { 425 426 /** 427 * Returns the class of the store immediately associated with this cache. 428 * @return string 429 */ 430 public function phpunit_get_store_class() { 431 return get_class($this->get_store()); 432 } 433 434 /** 435 * Returns all the interfaces the cache store implements. 436 * @return array 437 */ 438 public function phpunit_get_store_implements() { 439 return class_implements($this->get_store()); 440 } 441 442 /** 443 * Returns the given key directly from the static acceleration array. 444 * 445 * @param string $key 446 * @return false|mixed 447 */ 448 public function phpunit_static_acceleration_get($key) { 449 return $this->static_acceleration_get($key); 450 } 451 452 /** 453 * Purges only the static acceleration while leaving the rest of the store in tack. 454 * 455 * Used for behaving like you have loaded 2 pages, and reset static while the backing store 456 * still contains all the same data. 457 * 458 */ 459 public function phpunit_static_acceleration_purge() { 460 $this->static_acceleration_purge(); 461 } 462 } 463 464 /** 465 * PHPUnit session cache loader. 466 * 467 * Used to expose things we could not otherwise see within an session cache. 468 * 469 * @copyright 2012 Sam Hemelryk 470 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 471 */ 472 class cache_phpunit_session extends cache_session { 473 474 /** @var Static member used for emulating the behaviour of session_id() during the tests. */ 475 protected static $sessionidmockup = 'phpunitmockupsessionid'; 476 477 /** 478 * Returns the class of the store immediately associated with this cache. 479 * @return string 480 */ 481 public function phpunit_get_store_class() { 482 return get_class($this->get_store()); 483 } 484 485 /** 486 * Returns all the interfaces the cache store implements. 487 * @return array 488 */ 489 public function phpunit_get_store_implements() { 490 return class_implements($this->get_store()); 491 } 492 493 /** 494 * Provide access to the {@link cache_session::get_key_prefix()} method. 495 * 496 * @return string 497 */ 498 public function phpunit_get_key_prefix() { 499 return $this->get_key_prefix(); 500 } 501 502 /** 503 * Allows to inject the session identifier. 504 * 505 * @param string $sessionid 506 */ 507 public static function phpunit_mockup_session_id($sessionid) { 508 static::$sessionidmockup = $sessionid; 509 } 510 511 /** 512 * Override the parent behaviour so that it does not need the actual session_id() call. 513 */ 514 protected function set_session_id() { 515 $this->sessionid = static::$sessionidmockup; 516 } 517 } 518 519 /** 520 * PHPUnit request cache loader. 521 * 522 * Used to expose things we could not otherwise see within an request cache. 523 * 524 * @copyright 2012 Sam Hemelryk 525 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 526 */ 527 class cache_phpunit_request extends cache_request { 528 529 /** 530 * Returns the class of the store immediately associated with this cache. 531 * @return string 532 */ 533 public function phpunit_get_store_class() { 534 return get_class($this->get_store()); 535 } 536 537 /** 538 * Returns all the interfaces the cache store implements. 539 * @return array 540 */ 541 public function phpunit_get_store_implements() { 542 return class_implements($this->get_store()); 543 } 544 } 545 546 /** 547 * Dummy overridden cache loader class that we can use to test overriding loader functionality. 548 * 549 * @copyright 2012 Sam Hemelryk 550 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 551 */ 552 class cache_phpunit_dummy_overrideclass extends cache_application { 553 // Satisfying the code pre-checker is just part of my day job. 554 } 555 556 /** 557 * Cache PHPUnit specific factory. 558 * 559 * @copyright 2012 Sam Hemelryk 560 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 561 */ 562 class cache_phpunit_factory extends cache_factory { 563 /** 564 * Exposes the cache_factory's disable method. 565 * 566 * Perhaps one day that method will be made public, for the time being it is protected. 567 */ 568 public static function phpunit_disable() { 569 parent::disable(); 570 } 571 } 572 573 /** 574 * Cache PHPUnit specific Cache helper. 575 * 576 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk> 577 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 578 */ 579 class cache_phpunit_cache extends cache { 580 /** 581 * Make the changes which simulate a new request within the cache. 582 * This essentially resets currently held static values in the class, and increments the current timestamp. 583 */ 584 public static function simulate_new_request() { 585 self::$now += 0.1; 586 self::$purgetoken = null; 587 } 588 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body