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 53*1c0be3ebSAndreas Gohr self::setupDataDir(); 54*1c0be3ebSAndreas 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; 143c01cdb70SChristopher Smith $INPUT = new Input(); 144f8369d7dSTobias Sarnowski } 145db5867f1SAndreas Gohr 146db5867f1SAndreas Gohr /** 147*1c0be3ebSAndreas Gohr * Reinitialize the data directory for this class run 148*1c0be3ebSAndreas Gohr */ 149*1c0be3ebSAndreas Gohr public static function setupDataDir() { 150*1c0be3ebSAndreas Gohr // remove any leftovers from the last run 151*1c0be3ebSAndreas Gohr if(is_dir(DOKU_TMP_DATA)) { 152*1c0be3ebSAndreas Gohr // clear indexer data and cache 153*1c0be3ebSAndreas Gohr idx_get_indexer()->clear(); 154*1c0be3ebSAndreas Gohr TestUtils::rdelete(DOKU_TMP_DATA); 155*1c0be3ebSAndreas Gohr } 156*1c0be3ebSAndreas Gohr 157*1c0be3ebSAndreas Gohr // populate default dirs 158*1c0be3ebSAndreas Gohr TestUtils::rcopy(TMP_DIR, __DIR__ . '/../data/'); 159*1c0be3ebSAndreas Gohr } 160*1c0be3ebSAndreas Gohr 161*1c0be3ebSAndreas Gohr /** 162*1c0be3ebSAndreas Gohr * Reinitialize the conf directory for this class run 163*1c0be3ebSAndreas Gohr */ 164*1c0be3ebSAndreas Gohr public static function setupConfDir() { 165*1c0be3ebSAndreas Gohr $defaults = [ 166*1c0be3ebSAndreas Gohr 'acronyms.conf', 167*1c0be3ebSAndreas Gohr 'dokuwiki.php', 168*1c0be3ebSAndreas Gohr 'entities.conf', 169*1c0be3ebSAndreas Gohr 'interwiki.conf', 170*1c0be3ebSAndreas Gohr 'license.php', 171*1c0be3ebSAndreas Gohr 'manifest.json', 172*1c0be3ebSAndreas Gohr 'mediameta.php', 173*1c0be3ebSAndreas Gohr 'mime.conf', 174*1c0be3ebSAndreas Gohr 'plugins.php', 175*1c0be3ebSAndreas Gohr 'plugins.required.php', 176*1c0be3ebSAndreas Gohr 'scheme.conf', 177*1c0be3ebSAndreas Gohr 'smileys.conf', 178*1c0be3ebSAndreas Gohr 'wordblock.conf' 179*1c0be3ebSAndreas Gohr ]; 180*1c0be3ebSAndreas Gohr 181*1c0be3ebSAndreas Gohr // clear any leftovers 182*1c0be3ebSAndreas Gohr if(is_dir(DOKU_CONF)) { 183*1c0be3ebSAndreas Gohr TestUtils::rdelete(DOKU_CONF); 184*1c0be3ebSAndreas Gohr } 185*1c0be3ebSAndreas Gohr mkdir(DOKU_CONF); 186*1c0be3ebSAndreas Gohr 187*1c0be3ebSAndreas Gohr // copy defaults 188*1c0be3ebSAndreas Gohr foreach($defaults as $file) { 189*1c0be3ebSAndreas Gohr copy(DOKU_INC . '/conf/' . $file, DOKU_CONF . $file); 190*1c0be3ebSAndreas Gohr } 191*1c0be3ebSAndreas Gohr 192*1c0be3ebSAndreas Gohr // copy test files 193*1c0be3ebSAndreas Gohr TestUtils::rcopy(TMP_DIR, __DIR__ . '/../conf'); 194*1c0be3ebSAndreas Gohr } 195*1c0be3ebSAndreas Gohr 196*1c0be3ebSAndreas 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 } 237f8369d7dSTobias Sarnowski} 238