Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 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 * Hacks the in-memory configuration for a store. 254 * 255 * @param string $store Name of store to edit e.g. 'default_application' 256 * @param array $configchanges List of config changes 257 */ 258 public function phpunit_edit_store_config(string $store, array $configchanges): void { 259 foreach ($configchanges as $name => $value) { 260 $this->configstores[$store]['configuration'][$name] = $value; 261 } 262 } 263 264 /** 265 * Forcefully adds a session store. 266 * 267 * @param string $name 268 */ 269 public function phpunit_add_session_store($name) { 270 $this->configstores[$name] = array( 271 'name' => $name, 272 'plugin' => 'session', 273 'configuration' => array(), 274 'features' => 14, 275 'modes' => 2, 276 'default' => true, 277 'class' => 'cachestore_session', 278 'lock' => 'cachelock_file_default', 279 ); 280 } 281 282 /** 283 * Forcefully injects a definition => store mapping. 284 * 285 * This function does no validation, you should only be calling if it you know 286 * exactly what to expect. 287 * 288 * @param string $definition 289 * @param string $store 290 * @param int $sort 291 */ 292 public function phpunit_add_definition_mapping($definition, $store, $sort) { 293 $this->configdefinitionmappings[] = array( 294 'store' => $store, 295 'definition' => $definition, 296 'sort' => (int)$sort 297 ); 298 } 299 300 /** 301 * Overrides the default site identifier used by the Cache API so that we can be sure of what it is. 302 * 303 * @return string 304 */ 305 public function get_site_identifier() { 306 global $CFG; 307 return $CFG->wwwroot.'phpunit'; 308 } 309 310 /** 311 * Checks if the configuration file exists. 312 * 313 * @return bool True if it exists 314 */ 315 public static function config_file_exists() { 316 // Allow for late static binding by using static. 317 $configfilepath = static::get_config_file_path(); 318 319 // Invalidate opcode php cache, so we get correct status of file. 320 core_component::invalidate_opcode_php_cache($configfilepath); 321 return file_exists($configfilepath); 322 } 323 } 324 325 326 /** 327 * Dummy object for testing cacheable object interface and interaction 328 * 329 * Wake from cache needs specific testing at times to ensure that during multiple 330 * cache get() requests it's possible to verify that it's getting woken each time. 331 * 332 * @copyright 2012 Sam Hemelryk 333 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 334 */ 335 class cache_phpunit_dummy_object extends stdClass implements cacheable_object { 336 /** 337 * Test property 1 338 * @var string 339 */ 340 public $property1; 341 /** 342 * Test property 1 343 * @var string 344 */ 345 public $property2; 346 /** 347 * Test property time for verifying wake is run at each get() call. 348 * @var float 349 */ 350 public $propertytime; 351 /** 352 * Constructor 353 * @param string $property1 354 * @param string $property2 355 */ 356 public function __construct($property1, $property2, $propertytime = null) { 357 $this->property1 = $property1; 358 $this->property2 = $property2; 359 $this->propertytime = $propertytime === null ? microtime(true) : $propertytime; 360 } 361 /** 362 * Prepares this object for caching 363 * @return array 364 */ 365 public function prepare_to_cache() { 366 return array($this->property1.'_ptc', $this->property2.'_ptc', $this->propertytime); 367 } 368 /** 369 * Returns this object from the cache 370 * @param array $data 371 * @return cache_phpunit_dummy_object 372 */ 373 public static function wake_from_cache($data) { 374 $time = null; 375 if (!is_null($data[2])) { 376 // Windows 32bit microtime() resolution is 15ms, we ensure the time has moved forward. 377 do { 378 $time = microtime(true); 379 } while ($time == $data[2]); 380 381 } 382 return new cache_phpunit_dummy_object(array_shift($data).'_wfc', array_shift($data).'_wfc', $time); 383 } 384 } 385 386 /** 387 * Dummy data source object for testing data source interface and implementation 388 * 389 * @copyright 2012 Sam Hemelryk 390 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 391 */ 392 class cache_phpunit_dummy_datasource implements cache_data_source { 393 /** 394 * Returns an instance of this object for use with the cache. 395 * 396 * @param cache_definition $definition 397 * @return cache_phpunit_dummy_datasource 398 */ 399 public static function get_instance_for_cache(cache_definition $definition) { 400 return new cache_phpunit_dummy_datasource(); 401 } 402 403 /** 404 * Loads a key for the cache. 405 * 406 * @param string $key 407 * @return string 408 */ 409 public function load_for_cache($key) { 410 return $key.' has no value really.'; 411 } 412 413 /** 414 * Loads many keys for the cache 415 * 416 * @param array $keys 417 * @return array 418 */ 419 public function load_many_for_cache(array $keys) { 420 $return = array(); 421 foreach ($keys as $key) { 422 $return[$key] = $key.' has no value really.'; 423 } 424 return $return; 425 } 426 } 427 428 /** 429 * PHPUnit application cache loader. 430 * 431 * Used to expose things we could not otherwise see within an application cache. 432 * 433 * @copyright 2012 Sam Hemelryk 434 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 435 */ 436 class cache_phpunit_application extends cache_application { 437 438 /** 439 * Returns the class of the store immediately associated with this cache. 440 * @return string 441 */ 442 public function phpunit_get_store_class() { 443 return get_class($this->get_store()); 444 } 445 446 /** 447 * Returns all the interfaces the cache store implements. 448 * @return array 449 */ 450 public function phpunit_get_store_implements() { 451 return class_implements($this->get_store()); 452 } 453 454 /** 455 * Returns the given key directly from the static acceleration array. 456 * 457 * @param string $key 458 * @return false|mixed 459 */ 460 public function phpunit_static_acceleration_get($key) { 461 return $this->static_acceleration_get($key); 462 } 463 464 /** 465 * Purges only the static acceleration while leaving the rest of the store in tack. 466 * 467 * Used for behaving like you have loaded 2 pages, and reset static while the backing store 468 * still contains all the same data. 469 * 470 */ 471 public function phpunit_static_acceleration_purge() { 472 $this->static_acceleration_purge(); 473 } 474 } 475 476 /** 477 * PHPUnit session cache loader. 478 * 479 * Used to expose things we could not otherwise see within an session cache. 480 * 481 * @copyright 2012 Sam Hemelryk 482 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 483 */ 484 class cache_phpunit_session extends cache_session { 485 486 /** @var Static member used for emulating the behaviour of session_id() during the tests. */ 487 protected static $sessionidmockup = 'phpunitmockupsessionid'; 488 489 /** 490 * Returns the class of the store immediately associated with this cache. 491 * @return string 492 */ 493 public function phpunit_get_store_class() { 494 return get_class($this->get_store()); 495 } 496 497 /** 498 * Returns all the interfaces the cache store implements. 499 * @return array 500 */ 501 public function phpunit_get_store_implements() { 502 return class_implements($this->get_store()); 503 } 504 505 /** 506 * Provide access to the {@link cache_session::get_key_prefix()} method. 507 * 508 * @return string 509 */ 510 public function phpunit_get_key_prefix() { 511 return $this->get_key_prefix(); 512 } 513 514 /** 515 * Allows to inject the session identifier. 516 * 517 * @param string $sessionid 518 */ 519 public static function phpunit_mockup_session_id($sessionid) { 520 static::$sessionidmockup = $sessionid; 521 } 522 523 /** 524 * Override the parent behaviour so that it does not need the actual session_id() call. 525 */ 526 protected function set_session_id() { 527 $this->sessionid = static::$sessionidmockup; 528 } 529 } 530 531 /** 532 * PHPUnit request cache loader. 533 * 534 * Used to expose things we could not otherwise see within an request cache. 535 * 536 * @copyright 2012 Sam Hemelryk 537 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 538 */ 539 class cache_phpunit_request extends cache_request { 540 541 /** 542 * Returns the class of the store immediately associated with this cache. 543 * @return string 544 */ 545 public function phpunit_get_store_class() { 546 return get_class($this->get_store()); 547 } 548 549 /** 550 * Returns all the interfaces the cache store implements. 551 * @return array 552 */ 553 public function phpunit_get_store_implements() { 554 return class_implements($this->get_store()); 555 } 556 } 557 558 /** 559 * Dummy overridden cache loader class that we can use to test overriding loader functionality. 560 * 561 * @copyright 2012 Sam Hemelryk 562 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 563 */ 564 class cache_phpunit_dummy_overrideclass extends cache_application { 565 // Satisfying the code pre-checker is just part of my day job. 566 } 567 568 /** 569 * Cache PHPUnit specific factory. 570 * 571 * @copyright 2012 Sam Hemelryk 572 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 573 */ 574 class cache_phpunit_factory extends cache_factory { 575 /** 576 * Exposes the cache_factory's disable method. 577 * 578 * Perhaps one day that method will be made public, for the time being it is protected. 579 */ 580 public static function phpunit_disable() { 581 parent::disable(); 582 } 583 } 584 585 /** 586 * Cache PHPUnit specific Cache helper. 587 * 588 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk> 589 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 590 */ 591 class cache_phpunit_cache extends cache { 592 /** 593 * Make the changes which simulate a new request within the cache. 594 * This essentially resets currently held static values in the class, and increments the current timestamp. 595 */ 596 public static function simulate_new_request() { 597 self::$now += 0.1; 598 self::$purgetoken = null; 599 } 600 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body