expectException($class); if(!is_null($message)) { $this->expectExceptionMessage($message); } } /** * Setup the data directory * * This is ran before each test class */ public static function setUpBeforeClass() : void { // just to be safe not to delete something undefined later if(!defined('TMP_DIR')) die('no temporary directory'); if(!defined('DOKU_TMP_DATA')) die('no temporary data directory'); self::setupDataDir(); self::setupConfDir(); } /** * Reset the DokuWiki environment before each test run. Makes sure loaded config, * language and plugins are correct. * * @throws Exception if plugin actions fail * @return void */ public function setUp() : void { // reset execution time if it's enabled if(ini_get('max_execution_time') > 0) { set_time_limit(90); } // reload config global $conf, $config_cascade; $conf = array(); foreach (array('default','local','protected') as $config_group) { if (empty($config_cascade['main'][$config_group])) continue; foreach ($config_cascade['main'][$config_group] as $config_file) { if (file_exists($config_file)) { include($config_file); } } } // reload license config global $license; $license = array(); // load the license file(s) foreach (array('default','local') as $config_group) { if (empty($config_cascade['license'][$config_group])) continue; foreach ($config_cascade['license'][$config_group] as $config_file) { if(file_exists($config_file)){ include($config_file); } } } // reload some settings $conf['gzip_output'] &= (strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false); if($conf['compression'] == 'bz2' && !DOKU_HAS_BZIP) { $conf['compression'] = 'gz'; } if($conf['compression'] == 'gz' && !DOKU_HAS_GZIP) { $conf['compression'] = 0; } // make real paths and check them init_creationmodes(); init_paths(); init_files(); // reset loaded plugins global $plugin_controller_class, $plugin_controller; /** @var PluginController $plugin_controller */ $plugin_controller = new $plugin_controller_class(); // disable all non-default plugins global $default_plugins; foreach ($plugin_controller->getList() as $plugin) { if (!in_array($plugin, $default_plugins)) { if (!$plugin_controller->disable($plugin)) { throw new Exception('Could not disable plugin "'.$plugin.'"!'); } } } // disable and enable configured plugins foreach ($this->pluginsDisabled as $plugin) { if (!$plugin_controller->disable($plugin)) { throw new Exception('Could not disable plugin "'.$plugin.'"!'); } } foreach ($this->pluginsEnabled as $plugin) { /* enable() returns false but works... if (!$plugin_controller->enable($plugin)) { throw new Exception('Could not enable plugin "'.$plugin.'"!'); } */ $plugin_controller->enable($plugin); } // reset event handler global $EVENT_HANDLER; $EVENT_HANDLER = new EventHandler(); // reload language $local = $conf['lang']; Event::createAndTrigger('INIT_LANG_LOAD', $local, 'init_lang', true); global $INPUT; $INPUT = new \dokuwiki\Input\Input(); } /** * Reinitialize the data directory for this class run */ public static function setupDataDir() { // remove any leftovers from the last run if(is_dir(DOKU_TMP_DATA)) { // clear indexer data and cache idx_get_indexer()->clear(); TestUtils::rdelete(DOKU_TMP_DATA); } // populate default dirs TestUtils::rcopy(TMP_DIR, __DIR__ . '/../data/'); } /** * Reinitialize the conf directory for this class run */ public static function setupConfDir() { $defaults = [ 'acronyms.conf', 'dokuwiki.php', 'entities.conf', 'interwiki.conf', 'license.php', 'manifest.json', 'mediameta.php', 'mime.conf', 'plugins.php', 'plugins.required.php', 'scheme.conf', 'smileys.conf', 'wordblock.conf' ]; // clear any leftovers if(is_dir(DOKU_CONF)) { TestUtils::rdelete(DOKU_CONF); } mkdir(DOKU_CONF); // copy defaults foreach($defaults as $file) { copy(DOKU_INC . '/conf/' . $file, DOKU_CONF . $file); } // copy test files TestUtils::rcopy(TMP_DIR, __DIR__ . '/../conf'); } /** * Waits until a new second has passed * * This tried to be clever about the passing of time and return early if possible. Unfortunately * this never worked reliably for unknown reasons. To avoid flaky tests, this now always simply * sleeps for a full second on every call. * * @param bool $init no longer used * @return int new timestamp */ protected function waitForTick($init = false) { sleep(1); return time(); } /** * Allow for testing inaccessible methods (private or protected) * * This makes it easier to test protected methods without needing to create intermediate * classes inheriting and changing the access. * * @link https://stackoverflow.com/a/8702347/172068 * @param object $obj Object in which to call the method * @param string $func The method to call * @param array $args The arguments to call the method with * @return mixed * @throws ReflectionException when the given obj/func does not exist */ protected static function callInaccessibleMethod($obj, $func, array $args) { $class = new \ReflectionClass($obj); $method = $class->getMethod($func); $method->setAccessible(true); return $method->invokeArgs($obj, $args); } /** * Allow for reading inaccessible properties (private or protected) * * This makes it easier to check internals of tested objects. This should generally * be avoided. * * @param object $obj Object on which to access the property * @param string $prop name of the property to access * @return mixed * @throws ReflectionException when the given obj/prop does not exist */ protected static function getInaccessibleProperty($obj, $prop) { $class = new \ReflectionClass($obj); $property = $class->getProperty($prop); $property->setAccessible(true); return $property->getValue($obj); } /** * Allow for reading inaccessible properties (private or protected) * * This makes it easier to set internals of tested objects. This should generally * be avoided. * * @param object $obj Object on which to access the property * @param string $prop name of the property to access * @param mixed $value new value to set the property to * @return void * @throws ReflectionException when the given obj/prop does not exist */ protected static function setInaccessibleProperty($obj, $prop, $value) { $class = new \ReflectionClass($obj); $property = $class->getProperty($prop); $property->setAccessible(true); $property->setValue($obj, $value); } }