1f8369d7dSTobias Sarnowski<?php 201ef6ea2SAndreas Gohrif(!class_exists('PHPUnit_Framework_TestCase')) { 301ef6ea2SAndreas Gohr /** 401ef6ea2SAndreas Gohr * phpunit 5/6 compatibility 501ef6ea2SAndreas Gohr */ 601ef6ea2SAndreas Gohr class PHPUnit_Framework_TestCase extends PHPUnit\Framework\TestCase { 701ef6ea2SAndreas Gohr /** 83c1490b3SPhy * setExpectedException is deprecated in PHPUnit 6 93c1490b3SPhy * 1001ef6ea2SAndreas Gohr * @param string $class 1101ef6ea2SAndreas Gohr * @param null|string $message 1201ef6ea2SAndreas Gohr */ 1301ef6ea2SAndreas Gohr public function setExpectedException($class, $message=null) { 1401ef6ea2SAndreas Gohr $this->expectException($class); 1501ef6ea2SAndreas Gohr if(!is_null($message)) { 1601ef6ea2SAndreas Gohr $this->expectExceptionMessage($message); 1701ef6ea2SAndreas Gohr } 1801ef6ea2SAndreas Gohr } 1901ef6ea2SAndreas Gohr } 2001ef6ea2SAndreas Gohr} 2101ef6ea2SAndreas Gohr 22f8369d7dSTobias Sarnowski/** 23f8369d7dSTobias Sarnowski * Helper class to provide basic functionality for tests 243c1490b3SPhy * 253c1490b3SPhy * @uses PHPUnit_Framework_TestCase and thus PHPUnit 5.7+ is required 26f8369d7dSTobias Sarnowski */ 2701ef6ea2SAndreas Gohrabstract class DokuWikiTest extends PHPUnit_Framework_TestCase { 28f8369d7dSTobias Sarnowski 29f8369d7dSTobias Sarnowski /** 30f8369d7dSTobias Sarnowski * tests can override this 31f8369d7dSTobias Sarnowski * 32f8369d7dSTobias Sarnowski * @var array plugins to enable for test class 33f8369d7dSTobias Sarnowski */ 34f8369d7dSTobias Sarnowski protected $pluginsEnabled = array(); 35f8369d7dSTobias Sarnowski 36f8369d7dSTobias Sarnowski /** 37f8369d7dSTobias Sarnowski * tests can override this 38f8369d7dSTobias Sarnowski * 39f8369d7dSTobias Sarnowski * @var array plugins to disable for test class 40f8369d7dSTobias Sarnowski */ 41f8369d7dSTobias Sarnowski protected $pluginsDisabled = array(); 42f8369d7dSTobias Sarnowski 43f8369d7dSTobias Sarnowski /** 440644090aSAndreas Gohr * Setup the data directory 450644090aSAndreas Gohr * 460644090aSAndreas Gohr * This is ran before each test class 470644090aSAndreas Gohr */ 480644090aSAndreas Gohr public static function setUpBeforeClass() { 490644090aSAndreas Gohr // just to be safe not to delete something undefined later 500644090aSAndreas Gohr if(!defined('TMP_DIR')) die('no temporary directory'); 510644090aSAndreas Gohr if(!defined('DOKU_TMP_DATA')) die('no temporary data directory'); 520644090aSAndreas Gohr 531c0be3ebSAndreas Gohr self::setupDataDir(); 541c0be3ebSAndreas Gohr self::setupConfDir(); 550644090aSAndreas Gohr } 560644090aSAndreas Gohr 570644090aSAndreas Gohr /** 58f8369d7dSTobias Sarnowski * Reset the DokuWiki environment before each test run. Makes sure loaded config, 59f8369d7dSTobias Sarnowski * language and plugins are correct. 60f8369d7dSTobias Sarnowski * 61f8369d7dSTobias Sarnowski * @throws Exception if plugin actions fail 62f8369d7dSTobias Sarnowski * @return void 63f8369d7dSTobias Sarnowski */ 64f8369d7dSTobias Sarnowski public function setUp() { 650644090aSAndreas Gohr 66f8369d7dSTobias Sarnowski // reload config 67f8369d7dSTobias Sarnowski global $conf, $config_cascade; 68f8369d7dSTobias Sarnowski $conf = array(); 69f8369d7dSTobias Sarnowski foreach (array('default','local','protected') as $config_group) { 70f8369d7dSTobias Sarnowski if (empty($config_cascade['main'][$config_group])) continue; 71f8369d7dSTobias Sarnowski foreach ($config_cascade['main'][$config_group] as $config_file) { 7279e79377SAndreas Gohr if (file_exists($config_file)) { 73f8369d7dSTobias Sarnowski include($config_file); 74f8369d7dSTobias Sarnowski } 75f8369d7dSTobias Sarnowski } 76f8369d7dSTobias Sarnowski } 77f8369d7dSTobias Sarnowski 78f8369d7dSTobias Sarnowski // reload license config 79f8369d7dSTobias Sarnowski global $license; 80f8369d7dSTobias Sarnowski $license = array(); 81f8369d7dSTobias Sarnowski 82f8369d7dSTobias Sarnowski // load the license file(s) 83f8369d7dSTobias Sarnowski foreach (array('default','local') as $config_group) { 84f8369d7dSTobias Sarnowski if (empty($config_cascade['license'][$config_group])) continue; 85f8369d7dSTobias Sarnowski foreach ($config_cascade['license'][$config_group] as $config_file) { 8679e79377SAndreas Gohr if(file_exists($config_file)){ 87f8369d7dSTobias Sarnowski include($config_file); 88f8369d7dSTobias Sarnowski } 89f8369d7dSTobias Sarnowski } 90f8369d7dSTobias Sarnowski } 91f48e16abSGerrit Uitslag // reload some settings 92f48e16abSGerrit Uitslag $conf['gzip_output'] &= (strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false); 93f8369d7dSTobias Sarnowski 94f48e16abSGerrit Uitslag if($conf['compression'] == 'bz2' && !DOKU_HAS_BZIP) { 95f48e16abSGerrit Uitslag $conf['compression'] = 'gz'; 96f48e16abSGerrit Uitslag } 97f48e16abSGerrit Uitslag if($conf['compression'] == 'gz' && !DOKU_HAS_GZIP) { 98f48e16abSGerrit Uitslag $conf['compression'] = 0; 99f48e16abSGerrit Uitslag } 100f8369d7dSTobias Sarnowski // make real paths and check them 101f8369d7dSTobias Sarnowski init_paths(); 102f8369d7dSTobias Sarnowski init_files(); 103f8369d7dSTobias Sarnowski 104f8369d7dSTobias Sarnowski // reset loaded plugins 105f8369d7dSTobias Sarnowski global $plugin_controller_class, $plugin_controller; 106e3ab6fc5SMichael Hamann /** @var Doku_Plugin_Controller $plugin_controller */ 107f8369d7dSTobias Sarnowski $plugin_controller = new $plugin_controller_class(); 108f8369d7dSTobias Sarnowski 109f8369d7dSTobias Sarnowski // disable all non-default plugins 110f8369d7dSTobias Sarnowski global $default_plugins; 111f8369d7dSTobias Sarnowski foreach ($plugin_controller->getList() as $plugin) { 112f8369d7dSTobias Sarnowski if (!in_array($plugin, $default_plugins)) { 113f8369d7dSTobias Sarnowski if (!$plugin_controller->disable($plugin)) { 114f8369d7dSTobias Sarnowski throw new Exception('Could not disable plugin "'.$plugin.'"!'); 115f8369d7dSTobias Sarnowski } 116f8369d7dSTobias Sarnowski } 117f8369d7dSTobias Sarnowski } 118f8369d7dSTobias Sarnowski 119f8369d7dSTobias Sarnowski // disable and enable configured plugins 120f8369d7dSTobias Sarnowski foreach ($this->pluginsDisabled as $plugin) { 121f8369d7dSTobias Sarnowski if (!$plugin_controller->disable($plugin)) { 122f8369d7dSTobias Sarnowski throw new Exception('Could not disable plugin "'.$plugin.'"!'); 123f8369d7dSTobias Sarnowski } 124f8369d7dSTobias Sarnowski } 125f8369d7dSTobias Sarnowski foreach ($this->pluginsEnabled as $plugin) { 126f8369d7dSTobias Sarnowski /* enable() returns false but works... 127f8369d7dSTobias Sarnowski if (!$plugin_controller->enable($plugin)) { 128f8369d7dSTobias Sarnowski throw new Exception('Could not enable plugin "'.$plugin.'"!'); 129f8369d7dSTobias Sarnowski } 130f8369d7dSTobias Sarnowski */ 131f8369d7dSTobias Sarnowski $plugin_controller->enable($plugin); 132f8369d7dSTobias Sarnowski } 133f8369d7dSTobias Sarnowski 134f8369d7dSTobias Sarnowski // reset event handler 135f8369d7dSTobias Sarnowski global $EVENT_HANDLER; 136f8369d7dSTobias Sarnowski $EVENT_HANDLER = new Doku_Event_Handler(); 137f8369d7dSTobias Sarnowski 138f8369d7dSTobias Sarnowski // reload language 139f8369d7dSTobias Sarnowski $local = $conf['lang']; 140f8369d7dSTobias Sarnowski trigger_event('INIT_LANG_LOAD', $local, 'init_lang', true); 141c01cdb70SChristopher Smith 142c01cdb70SChristopher Smith global $INPUT; 143*ccc4c71cSAndreas Gohr $INPUT = new \dokuwiki\Input\Input(); 144f8369d7dSTobias Sarnowski } 145db5867f1SAndreas Gohr 146db5867f1SAndreas Gohr /** 1471c0be3ebSAndreas Gohr * Reinitialize the data directory for this class run 1481c0be3ebSAndreas Gohr */ 1491c0be3ebSAndreas Gohr public static function setupDataDir() { 1501c0be3ebSAndreas Gohr // remove any leftovers from the last run 1511c0be3ebSAndreas Gohr if(is_dir(DOKU_TMP_DATA)) { 1521c0be3ebSAndreas Gohr // clear indexer data and cache 1531c0be3ebSAndreas Gohr idx_get_indexer()->clear(); 1541c0be3ebSAndreas Gohr TestUtils::rdelete(DOKU_TMP_DATA); 1551c0be3ebSAndreas Gohr } 1561c0be3ebSAndreas Gohr 1571c0be3ebSAndreas Gohr // populate default dirs 1581c0be3ebSAndreas Gohr TestUtils::rcopy(TMP_DIR, __DIR__ . '/../data/'); 1591c0be3ebSAndreas Gohr } 1601c0be3ebSAndreas Gohr 1611c0be3ebSAndreas Gohr /** 1621c0be3ebSAndreas Gohr * Reinitialize the conf directory for this class run 1631c0be3ebSAndreas Gohr */ 1641c0be3ebSAndreas Gohr public static function setupConfDir() { 1651c0be3ebSAndreas Gohr $defaults = [ 1661c0be3ebSAndreas Gohr 'acronyms.conf', 1671c0be3ebSAndreas Gohr 'dokuwiki.php', 1681c0be3ebSAndreas Gohr 'entities.conf', 1691c0be3ebSAndreas Gohr 'interwiki.conf', 1701c0be3ebSAndreas Gohr 'license.php', 1711c0be3ebSAndreas Gohr 'manifest.json', 1721c0be3ebSAndreas Gohr 'mediameta.php', 1731c0be3ebSAndreas Gohr 'mime.conf', 1741c0be3ebSAndreas Gohr 'plugins.php', 1751c0be3ebSAndreas Gohr 'plugins.required.php', 1761c0be3ebSAndreas Gohr 'scheme.conf', 1771c0be3ebSAndreas Gohr 'smileys.conf', 1781c0be3ebSAndreas Gohr 'wordblock.conf' 1791c0be3ebSAndreas Gohr ]; 1801c0be3ebSAndreas Gohr 1811c0be3ebSAndreas Gohr // clear any leftovers 1821c0be3ebSAndreas Gohr if(is_dir(DOKU_CONF)) { 1831c0be3ebSAndreas Gohr TestUtils::rdelete(DOKU_CONF); 1841c0be3ebSAndreas Gohr } 1851c0be3ebSAndreas Gohr mkdir(DOKU_CONF); 1861c0be3ebSAndreas Gohr 1871c0be3ebSAndreas Gohr // copy defaults 1881c0be3ebSAndreas Gohr foreach($defaults as $file) { 1891c0be3ebSAndreas Gohr copy(DOKU_INC . '/conf/' . $file, DOKU_CONF . $file); 1901c0be3ebSAndreas Gohr } 1911c0be3ebSAndreas Gohr 1921c0be3ebSAndreas Gohr // copy test files 1931c0be3ebSAndreas Gohr TestUtils::rcopy(TMP_DIR, __DIR__ . '/../conf'); 1941c0be3ebSAndreas Gohr } 1951c0be3ebSAndreas Gohr 1961c0be3ebSAndreas Gohr /** 197d732617bSAndreas Gohr * Waits until a new second has passed 198d732617bSAndreas Gohr * 199d732617bSAndreas Gohr * The very first call will return immeadiately, proceeding calls will return 200d732617bSAndreas Gohr * only after at least 1 second after the last call has passed. 201d732617bSAndreas Gohr * 202d732617bSAndreas Gohr * When passing $init=true it will not return immeadiately but use the current 203d732617bSAndreas Gohr * second as initialization. It might still return faster than a second. 204d732617bSAndreas Gohr * 205d732617bSAndreas Gohr * @param bool $init wait from now on, not from last time 206d732617bSAndreas Gohr * @return int new timestamp 207d732617bSAndreas Gohr */ 208d732617bSAndreas Gohr protected function waitForTick($init = false) { 209d732617bSAndreas Gohr static $last = 0; 210d732617bSAndreas Gohr if($init) $last = time(); 211d732617bSAndreas Gohr while($last === $now = time()) { 212d732617bSAndreas Gohr usleep(100000); //recheck in a 10th of a second 213d732617bSAndreas Gohr } 214d732617bSAndreas Gohr $last = $now; 215d732617bSAndreas Gohr return $now; 216d732617bSAndreas Gohr } 217210ff133SAndreas Gohr 218210ff133SAndreas Gohr /** 219210ff133SAndreas Gohr * Allow for testing inaccessible methods (private or protected) 220210ff133SAndreas Gohr * 221210ff133SAndreas Gohr * This makes it easier to test protected methods without needing to create intermediate 222210ff133SAndreas Gohr * classes inheriting and changing the access. 223210ff133SAndreas Gohr * 224210ff133SAndreas Gohr * @link https://stackoverflow.com/a/8702347/172068 225210ff133SAndreas Gohr * @param object $obj Object in which to call the method 226210ff133SAndreas Gohr * @param string $func The method to call 227210ff133SAndreas Gohr * @param array $args The arguments to call the method with 228210ff133SAndreas Gohr * @return mixed 229210ff133SAndreas Gohr * @throws ReflectionException when the given obj/func does not exist 230210ff133SAndreas Gohr */ 231210ff133SAndreas Gohr protected static function callInaccessibleMethod($obj, $func, array $args) { 232210ff133SAndreas Gohr $class = new \ReflectionClass($obj); 233210ff133SAndreas Gohr $method = $class->getMethod($func); 234210ff133SAndreas Gohr $method->setAccessible(true); 235210ff133SAndreas Gohr return $method->invokeArgs($obj, $args); 236210ff133SAndreas Gohr } 237836f6efbSAndreas Gohr 238836f6efbSAndreas Gohr /** 239836f6efbSAndreas Gohr * Allow for reading inaccessible properties (private or protected) 240836f6efbSAndreas Gohr * 241836f6efbSAndreas Gohr * This makes it easier to check internals of tested objects. This should generally 242836f6efbSAndreas Gohr * be avoided. 243836f6efbSAndreas Gohr * 244836f6efbSAndreas Gohr * @param object $obj Object on which to access the property 245836f6efbSAndreas Gohr * @param string $prop name of the property to access 246836f6efbSAndreas Gohr * @return mixed 247836f6efbSAndreas Gohr * @throws ReflectionException when the given obj/prop does not exist 248836f6efbSAndreas Gohr */ 249836f6efbSAndreas Gohr protected static function getInaccessibleProperty($obj, $prop) { 250836f6efbSAndreas Gohr $class = new \ReflectionClass($obj); 251836f6efbSAndreas Gohr $property = $class->getProperty($prop); 252836f6efbSAndreas Gohr $property->setAccessible(true); 253836f6efbSAndreas Gohr return $property->getValue($obj); 254836f6efbSAndreas Gohr } 255836f6efbSAndreas Gohr 256836f6efbSAndreas Gohr /** 257836f6efbSAndreas Gohr * Allow for reading inaccessible properties (private or protected) 258836f6efbSAndreas Gohr * 259836f6efbSAndreas Gohr * This makes it easier to set internals of tested objects. This should generally 260836f6efbSAndreas Gohr * be avoided. 261836f6efbSAndreas Gohr * 262836f6efbSAndreas Gohr * @param object $obj Object on which to access the property 263836f6efbSAndreas Gohr * @param string $prop name of the property to access 264836f6efbSAndreas Gohr * @param mixed $value new value to set the property to 265836f6efbSAndreas Gohr * @return void 266836f6efbSAndreas Gohr * @throws ReflectionException when the given obj/prop does not exist 267836f6efbSAndreas Gohr */ 268836f6efbSAndreas Gohr protected static function setInaccessibleProperty($obj, $prop, $value) { 269836f6efbSAndreas Gohr $class = new \ReflectionClass($obj); 270836f6efbSAndreas Gohr $property = $class->getProperty($prop); 271836f6efbSAndreas Gohr $property->setAccessible(true); 272836f6efbSAndreas Gohr $property->setValue($obj, $value); 273836f6efbSAndreas Gohr } 274f8369d7dSTobias Sarnowski} 275