dokuwiki/_test/core/DokuWikiTest.php

270 lines
8.6 KiB
PHP

<?php
use dokuwiki\Extension\PluginController;
use dokuwiki\Extension\Event;
use dokuwiki\Extension\EventHandler;
/**
* Helper class to provide basic functionality for tests
*
* @uses PHPUnit_Framework_TestCase and thus PHPUnit 5.7+ is required
*/
abstract class DokuWikiTest extends PHPUnit\Framework\TestCase {
/**
* tests can override this
*
* @var array plugins to enable for test class
*/
protected $pluginsEnabled = array();
/**
* tests can override this
*
* @var array plugins to disable for test class
*/
protected $pluginsDisabled = array();
/**
* setExpectedException was deprecated in PHPUnit 6
*
* @param string $class
* @param null|string $message
*/
public function setExpectedException($class, $message=null) {
$this->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);
}
}