1<?php 2if(!class_exists('PHPUnit_Framework_TestCase')) { 3 /** 4 * phpunit 5/6 compatibility 5 */ 6 class PHPUnit_Framework_TestCase extends PHPUnit\Framework\TestCase { 7 /** 8 * setExpectedException is deprecated in PHPUnit 6 9 * 10 * @param string $class 11 * @param null|string $message 12 */ 13 public function setExpectedException($class, $message=null) { 14 $this->expectException($class); 15 if(!is_null($message)) { 16 $this->expectExceptionMessage($message); 17 } 18 } 19 } 20} 21 22/** 23 * Helper class to provide basic functionality for tests 24 * 25 * @uses PHPUnit_Framework_TestCase and thus PHPUnit 5.7+ is required 26 */ 27abstract class DokuWikiTest extends PHPUnit_Framework_TestCase { 28 29 /** 30 * tests can override this 31 * 32 * @var array plugins to enable for test class 33 */ 34 protected $pluginsEnabled = array(); 35 36 /** 37 * tests can override this 38 * 39 * @var array plugins to disable for test class 40 */ 41 protected $pluginsDisabled = array(); 42 43 /** 44 * Setup the data directory 45 * 46 * This is ran before each test class 47 */ 48 public static function setUpBeforeClass() { 49 // just to be safe not to delete something undefined later 50 if(!defined('TMP_DIR')) die('no temporary directory'); 51 if(!defined('DOKU_TMP_DATA')) die('no temporary data directory'); 52 53 // remove any leftovers from the last run 54 if(is_dir(DOKU_TMP_DATA)){ 55 // clear indexer data and cache 56 idx_get_indexer()->clear(); 57 TestUtils::rdelete(DOKU_TMP_DATA); 58 } 59 60 // populate default dirs 61 TestUtils::rcopy(TMP_DIR, dirname(__FILE__).'/../data/'); 62 } 63 64 /** 65 * Reset the DokuWiki environment before each test run. Makes sure loaded config, 66 * language and plugins are correct. 67 * 68 * @throws Exception if plugin actions fail 69 * @return void 70 */ 71 public function setUp() { 72 73 // reload config 74 global $conf, $config_cascade; 75 $conf = array(); 76 foreach (array('default','local','protected') as $config_group) { 77 if (empty($config_cascade['main'][$config_group])) continue; 78 foreach ($config_cascade['main'][$config_group] as $config_file) { 79 if (file_exists($config_file)) { 80 include($config_file); 81 } 82 } 83 } 84 85 // reload license config 86 global $license; 87 $license = array(); 88 89 // load the license file(s) 90 foreach (array('default','local') as $config_group) { 91 if (empty($config_cascade['license'][$config_group])) continue; 92 foreach ($config_cascade['license'][$config_group] as $config_file) { 93 if(file_exists($config_file)){ 94 include($config_file); 95 } 96 } 97 } 98 // reload some settings 99 $conf['gzip_output'] &= (strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false); 100 101 if($conf['compression'] == 'bz2' && !DOKU_HAS_BZIP) { 102 $conf['compression'] = 'gz'; 103 } 104 if($conf['compression'] == 'gz' && !DOKU_HAS_GZIP) { 105 $conf['compression'] = 0; 106 } 107 // make real paths and check them 108 init_paths(); 109 init_files(); 110 111 // reset loaded plugins 112 global $plugin_controller_class, $plugin_controller; 113 /** @var Doku_Plugin_Controller $plugin_controller */ 114 $plugin_controller = new $plugin_controller_class(); 115 116 // disable all non-default plugins 117 global $default_plugins; 118 foreach ($plugin_controller->getList() as $plugin) { 119 if (!in_array($plugin, $default_plugins)) { 120 if (!$plugin_controller->disable($plugin)) { 121 throw new Exception('Could not disable plugin "'.$plugin.'"!'); 122 } 123 } 124 } 125 126 // disable and enable configured plugins 127 foreach ($this->pluginsDisabled as $plugin) { 128 if (!$plugin_controller->disable($plugin)) { 129 throw new Exception('Could not disable plugin "'.$plugin.'"!'); 130 } 131 } 132 foreach ($this->pluginsEnabled as $plugin) { 133 /* enable() returns false but works... 134 if (!$plugin_controller->enable($plugin)) { 135 throw new Exception('Could not enable plugin "'.$plugin.'"!'); 136 } 137 */ 138 $plugin_controller->enable($plugin); 139 } 140 141 // reset event handler 142 global $EVENT_HANDLER; 143 $EVENT_HANDLER = new Doku_Event_Handler(); 144 145 // reload language 146 $local = $conf['lang']; 147 trigger_event('INIT_LANG_LOAD', $local, 'init_lang', true); 148 149 global $INPUT; 150 $INPUT = new Input(); 151 } 152 153 /** 154 * Waits until a new second has passed 155 * 156 * The very first call will return immeadiately, proceeding calls will return 157 * only after at least 1 second after the last call has passed. 158 * 159 * When passing $init=true it will not return immeadiately but use the current 160 * second as initialization. It might still return faster than a second. 161 * 162 * @param bool $init wait from now on, not from last time 163 * @return int new timestamp 164 */ 165 protected function waitForTick($init = false) { 166 static $last = 0; 167 if($init) $last = time(); 168 while($last === $now = time()) { 169 usleep(100000); //recheck in a 10th of a second 170 } 171 $last = $now; 172 return $now; 173 } 174 175 /** 176 * Allow for testing inaccessible methods (private or protected) 177 * 178 * This makes it easier to test protected methods without needing to create intermediate 179 * classes inheriting and changing the access. 180 * 181 * @link https://stackoverflow.com/a/8702347/172068 182 * @param object $obj Object in which to call the method 183 * @param string $func The method to call 184 * @param array $args The arguments to call the method with 185 * @return mixed 186 * @throws ReflectionException when the given obj/func does not exist 187 */ 188 protected static function callInaccessibleMethod($obj, $func, array $args) { 189 $class = new \ReflectionClass($obj); 190 $method = $class->getMethod($func); 191 $method->setAccessible(true); 192 return $method->invokeArgs($obj, $args); 193 } 194 195 /** 196 * Allow for reading inaccessible properties (private or protected) 197 * 198 * This makes it easier to check internals of tested objects. This should generally 199 * be avoided. 200 * 201 * @param object $obj Object on which to access the property 202 * @param string $prop name of the property to access 203 * @return mixed 204 * @throws ReflectionException when the given obj/prop does not exist 205 */ 206 protected static function getInaccessibleProperty($obj, $prop) { 207 $class = new \ReflectionClass($obj); 208 $property = $class->getProperty($prop); 209 $property->setAccessible(true); 210 return $property->getValue($obj); 211 } 212 213 /** 214 * Allow for reading inaccessible properties (private or protected) 215 * 216 * This makes it easier to set internals of tested objects. This should generally 217 * be avoided. 218 * 219 * @param object $obj Object on which to access the property 220 * @param string $prop name of the property to access 221 * @param mixed $value new value to set the property to 222 * @return void 223 * @throws ReflectionException when the given obj/prop does not exist 224 */ 225 protected static function setInaccessibleProperty($obj, $prop, $value) { 226 $class = new \ReflectionClass($obj); 227 $property = $class->getProperty($prop); 228 $property->setAccessible(true); 229 $property->setValue($obj, $value); 230 } 231} 232