[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 3 use dokuwiki\Extension\PluginController; 4 use dokuwiki\Extension\Event; 5 use dokuwiki\Extension\EventHandler; 6 /** 7 * Helper class to provide basic functionality for tests 8 * 9 * @uses PHPUnit_Framework_TestCase and thus PHPUnit 5.7+ is required 10 */ 11 abstract class DokuWikiTest extends PHPUnit\Framework\TestCase { 12 13 /** 14 * tests can override this 15 * 16 * @var array plugins to enable for test class 17 */ 18 protected $pluginsEnabled = array(); 19 20 /** 21 * tests can override this 22 * 23 * @var array plugins to disable for test class 24 */ 25 protected $pluginsDisabled = array(); 26 27 /** 28 * setExpectedException was deprecated in PHPUnit 6 29 * 30 * @param string $class 31 * @param null|string $message 32 */ 33 public function setExpectedException($class, $message=null) { 34 $this->expectException($class); 35 if(!is_null($message)) { 36 $this->expectExceptionMessage($message); 37 } 38 } 39 40 /** 41 * Setup the data directory 42 * 43 * This is ran before each test class 44 */ 45 public static function setUpBeforeClass() : void { 46 // just to be safe not to delete something undefined later 47 if(!defined('TMP_DIR')) die('no temporary directory'); 48 if(!defined('DOKU_TMP_DATA')) die('no temporary data directory'); 49 50 self::setupDataDir(); 51 self::setupConfDir(); 52 } 53 54 /** 55 * Reset the DokuWiki environment before each test run. Makes sure loaded config, 56 * language and plugins are correct. 57 * 58 * @throws Exception if plugin actions fail 59 * @return void 60 */ 61 public function setUp() : void { 62 // reset execution time if it's enabled 63 if(ini_get('max_execution_time') > 0) { 64 set_time_limit(90); 65 } 66 67 // reload config 68 global $conf, $config_cascade; 69 $conf = array(); 70 foreach (array('default','local','protected') as $config_group) { 71 if (empty($config_cascade['main'][$config_group])) continue; 72 foreach ($config_cascade['main'][$config_group] as $config_file) { 73 if (file_exists($config_file)) { 74 include($config_file); 75 } 76 } 77 } 78 79 // reload license config 80 global $license; 81 $license = array(); 82 83 // load the license file(s) 84 foreach (array('default','local') as $config_group) { 85 if (empty($config_cascade['license'][$config_group])) continue; 86 foreach ($config_cascade['license'][$config_group] as $config_file) { 87 if(file_exists($config_file)){ 88 include($config_file); 89 } 90 } 91 } 92 // reload some settings 93 $conf['gzip_output'] &= (strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false); 94 95 if($conf['compression'] == 'bz2' && !DOKU_HAS_BZIP) { 96 $conf['compression'] = 'gz'; 97 } 98 if($conf['compression'] == 'gz' && !DOKU_HAS_GZIP) { 99 $conf['compression'] = 0; 100 } 101 // make real paths and check them 102 init_creationmodes(); 103 init_paths(); 104 init_files(); 105 106 // reset loaded plugins 107 global $plugin_controller_class, $plugin_controller; 108 /** @var PluginController $plugin_controller */ 109 $plugin_controller = new $plugin_controller_class(); 110 111 // disable all non-default plugins 112 global $default_plugins; 113 foreach ($plugin_controller->getList() as $plugin) { 114 if (!in_array($plugin, $default_plugins)) { 115 if (!$plugin_controller->disable($plugin)) { 116 throw new Exception('Could not disable plugin "'.$plugin.'"!'); 117 } 118 } 119 } 120 121 // disable and enable configured plugins 122 foreach ($this->pluginsDisabled as $plugin) { 123 if (!$plugin_controller->disable($plugin)) { 124 throw new Exception('Could not disable plugin "'.$plugin.'"!'); 125 } 126 } 127 foreach ($this->pluginsEnabled as $plugin) { 128 /* enable() returns false but works... 129 if (!$plugin_controller->enable($plugin)) { 130 throw new Exception('Could not enable plugin "'.$plugin.'"!'); 131 } 132 */ 133 $plugin_controller->enable($plugin); 134 } 135 136 // reset event handler 137 global $EVENT_HANDLER; 138 $EVENT_HANDLER = new EventHandler(); 139 140 // reload language 141 $local = $conf['lang']; 142 Event::createAndTrigger('INIT_LANG_LOAD', $local, 'init_lang', true); 143 144 global $INPUT; 145 $INPUT = new \dokuwiki\Input\Input(); 146 } 147 148 /** 149 * Reinitialize the data directory for this class run 150 */ 151 public static function setupDataDir() { 152 // remove any leftovers from the last run 153 if(is_dir(DOKU_TMP_DATA)) { 154 // clear indexer data and cache 155 idx_get_indexer()->clear(); 156 TestUtils::rdelete(DOKU_TMP_DATA); 157 } 158 159 // populate default dirs 160 TestUtils::rcopy(TMP_DIR, __DIR__ . '/../data/'); 161 } 162 163 /** 164 * Reinitialize the conf directory for this class run 165 */ 166 public static function setupConfDir() { 167 $defaults = [ 168 'acronyms.conf', 169 'dokuwiki.php', 170 'entities.conf', 171 'interwiki.conf', 172 'license.php', 173 'manifest.json', 174 'mediameta.php', 175 'mime.conf', 176 'plugins.php', 177 'plugins.required.php', 178 'scheme.conf', 179 'smileys.conf', 180 'wordblock.conf' 181 ]; 182 183 // clear any leftovers 184 if(is_dir(DOKU_CONF)) { 185 TestUtils::rdelete(DOKU_CONF); 186 } 187 mkdir(DOKU_CONF); 188 189 // copy defaults 190 foreach($defaults as $file) { 191 copy(DOKU_INC . '/conf/' . $file, DOKU_CONF . $file); 192 } 193 194 // copy test files 195 TestUtils::rcopy(TMP_DIR, __DIR__ . '/../conf'); 196 } 197 198 /** 199 * Waits until a new second has passed 200 * 201 * This tried to be clever about the passing of time and return early if possible. Unfortunately 202 * this never worked reliably for unknown reasons. To avoid flaky tests, this now always simply 203 * sleeps for a full second on every call. 204 * 205 * @param bool $init no longer used 206 * @return int new timestamp 207 */ 208 protected function waitForTick($init = false) { 209 sleep(1); 210 return time(); 211 } 212 213 /** 214 * Allow for testing inaccessible methods (private or protected) 215 * 216 * This makes it easier to test protected methods without needing to create intermediate 217 * classes inheriting and changing the access. 218 * 219 * @link https://stackoverflow.com/a/8702347/172068 220 * @param object $obj Object in which to call the method 221 * @param string $func The method to call 222 * @param array $args The arguments to call the method with 223 * @return mixed 224 * @throws ReflectionException when the given obj/func does not exist 225 */ 226 protected static function callInaccessibleMethod($obj, $func, array $args) { 227 $class = new \ReflectionClass($obj); 228 $method = $class->getMethod($func); 229 $method->setAccessible(true); 230 return $method->invokeArgs($obj, $args); 231 } 232 233 /** 234 * Allow for reading inaccessible properties (private or protected) 235 * 236 * This makes it easier to check internals of tested objects. This should generally 237 * be avoided. 238 * 239 * @param object $obj Object on which to access the property 240 * @param string $prop name of the property to access 241 * @return mixed 242 * @throws ReflectionException when the given obj/prop does not exist 243 */ 244 protected static function getInaccessibleProperty($obj, $prop) { 245 $class = new \ReflectionClass($obj); 246 $property = $class->getProperty($prop); 247 $property->setAccessible(true); 248 return $property->getValue($obj); 249 } 250 251 /** 252 * Allow for reading inaccessible properties (private or protected) 253 * 254 * This makes it easier to set internals of tested objects. This should generally 255 * be avoided. 256 * 257 * @param object $obj Object on which to access the property 258 * @param string $prop name of the property to access 259 * @param mixed $value new value to set the property to 260 * @return void 261 * @throws ReflectionException when the given obj/prop does not exist 262 */ 263 protected static function setInaccessibleProperty($obj, $prop, $value) { 264 $class = new \ReflectionClass($obj); 265 $property = $class->getProperty($prop); 266 $property->setAccessible(true); 267 $property->setValue($obj, $value); 268 } 269 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body