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