xref: /dokuwiki/_test/core/DokuWikiTest.php (revision dfa189a85768a42e53182a27d300bd6d69cdcc93)
1f8369d7dSTobias Sarnowski<?php
2e1d9dcc8SAndreas Gohr
33a7140a1SAndreas Gohruse dokuwiki\Extension\PluginController;
4cbb44eabSAndreas Gohruse dokuwiki\Extension\Event;
5e1d9dcc8SAndreas Gohruse dokuwiki\Extension\EventHandler;
6f8369d7dSTobias Sarnowski/**
7f8369d7dSTobias Sarnowski * Helper class to provide basic functionality for tests
83c1490b3SPhy *
93c1490b3SPhy * @uses PHPUnit_Framework_TestCase and thus PHPUnit 5.7+ is required
10f8369d7dSTobias Sarnowski */
11bd9dab32SAndreas Gohrabstract class DokuWikiTest extends PHPUnit\Framework\TestCase {
12f8369d7dSTobias Sarnowski
13f8369d7dSTobias Sarnowski    /**
14f8369d7dSTobias Sarnowski     * tests can override this
15f8369d7dSTobias Sarnowski     *
16f8369d7dSTobias Sarnowski     * @var array plugins to enable for test class
17f8369d7dSTobias Sarnowski     */
18f8369d7dSTobias Sarnowski    protected $pluginsEnabled = array();
19f8369d7dSTobias Sarnowski
20f8369d7dSTobias Sarnowski    /**
21f8369d7dSTobias Sarnowski     * tests can override this
22f8369d7dSTobias Sarnowski     *
23f8369d7dSTobias Sarnowski     * @var array plugins to disable for test class
24f8369d7dSTobias Sarnowski     */
25f8369d7dSTobias Sarnowski    protected $pluginsDisabled = array();
26f8369d7dSTobias Sarnowski
27f8369d7dSTobias Sarnowski    /**
28bd9dab32SAndreas Gohr     * setExpectedException was deprecated in PHPUnit 6
29bd9dab32SAndreas Gohr     *
30bd9dab32SAndreas Gohr     * @param string $class
31bd9dab32SAndreas Gohr     * @param null|string $message
32bd9dab32SAndreas Gohr     */
33bd9dab32SAndreas Gohr    public function setExpectedException($class, $message=null) {
34bd9dab32SAndreas Gohr        $this->expectException($class);
35bd9dab32SAndreas Gohr        if(!is_null($message)) {
36bd9dab32SAndreas Gohr            $this->expectExceptionMessage($message);
37bd9dab32SAndreas Gohr        }
38bd9dab32SAndreas Gohr    }
39bd9dab32SAndreas Gohr
40bd9dab32SAndreas Gohr    /**
410644090aSAndreas Gohr     * Setup the data directory
420644090aSAndreas Gohr     *
430644090aSAndreas Gohr     * This is ran before each test class
440644090aSAndreas Gohr     */
451c33cec3SAndreas Gohr    public static function setUpBeforeClass() : void {
460644090aSAndreas Gohr        // just to be safe not to delete something undefined later
470644090aSAndreas Gohr        if(!defined('TMP_DIR')) die('no temporary directory');
480644090aSAndreas Gohr        if(!defined('DOKU_TMP_DATA')) die('no temporary data directory');
490644090aSAndreas Gohr
501c0be3ebSAndreas Gohr        self::setupDataDir();
511c0be3ebSAndreas Gohr        self::setupConfDir();
520644090aSAndreas Gohr    }
530644090aSAndreas Gohr
540644090aSAndreas Gohr    /**
55f8369d7dSTobias Sarnowski     * Reset the DokuWiki environment before each test run. Makes sure loaded config,
56f8369d7dSTobias Sarnowski     * language and plugins are correct.
57f8369d7dSTobias Sarnowski     *
58f8369d7dSTobias Sarnowski     * @throws Exception if plugin actions fail
59f8369d7dSTobias Sarnowski     * @return void
60f8369d7dSTobias Sarnowski     */
611c33cec3SAndreas Gohr    public function setUp() : void {
62*dfa189a8SAndreas Gohr        // reset execution time if it's enabled
63*dfa189a8SAndreas Gohr        if(ini_get('max_execution_time') > 0) {
64*dfa189a8SAndreas Gohr            set_time_limit(90);
65*dfa189a8SAndreas Gohr        }
660644090aSAndreas Gohr
67f8369d7dSTobias Sarnowski        // reload config
68f8369d7dSTobias Sarnowski        global $conf, $config_cascade;
69f8369d7dSTobias Sarnowski        $conf = array();
70f8369d7dSTobias Sarnowski        foreach (array('default','local','protected') as $config_group) {
71f8369d7dSTobias Sarnowski            if (empty($config_cascade['main'][$config_group])) continue;
72f8369d7dSTobias Sarnowski            foreach ($config_cascade['main'][$config_group] as $config_file) {
7379e79377SAndreas Gohr                if (file_exists($config_file)) {
74f8369d7dSTobias Sarnowski                    include($config_file);
75f8369d7dSTobias Sarnowski                }
76f8369d7dSTobias Sarnowski            }
77f8369d7dSTobias Sarnowski        }
78f8369d7dSTobias Sarnowski
79f8369d7dSTobias Sarnowski        // reload license config
80f8369d7dSTobias Sarnowski        global $license;
81f8369d7dSTobias Sarnowski        $license = array();
82f8369d7dSTobias Sarnowski
83f8369d7dSTobias Sarnowski        // load the license file(s)
84f8369d7dSTobias Sarnowski        foreach (array('default','local') as $config_group) {
85f8369d7dSTobias Sarnowski            if (empty($config_cascade['license'][$config_group])) continue;
86f8369d7dSTobias Sarnowski            foreach ($config_cascade['license'][$config_group] as $config_file) {
8779e79377SAndreas Gohr                if(file_exists($config_file)){
88f8369d7dSTobias Sarnowski                    include($config_file);
89f8369d7dSTobias Sarnowski                }
90f8369d7dSTobias Sarnowski            }
91f8369d7dSTobias Sarnowski        }
92f48e16abSGerrit Uitslag        // reload some settings
93f48e16abSGerrit Uitslag        $conf['gzip_output'] &= (strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false);
94f8369d7dSTobias Sarnowski
95f48e16abSGerrit Uitslag        if($conf['compression'] == 'bz2' && !DOKU_HAS_BZIP) {
96f48e16abSGerrit Uitslag            $conf['compression'] = 'gz';
97f48e16abSGerrit Uitslag        }
98f48e16abSGerrit Uitslag        if($conf['compression'] == 'gz' && !DOKU_HAS_GZIP) {
99f48e16abSGerrit Uitslag            $conf['compression'] = 0;
100f48e16abSGerrit Uitslag        }
101f8369d7dSTobias Sarnowski        // make real paths and check them
1023366d071SAndreas Gohr        init_creationmodes();
103f8369d7dSTobias Sarnowski        init_paths();
104f8369d7dSTobias Sarnowski        init_files();
105f8369d7dSTobias Sarnowski
106f8369d7dSTobias Sarnowski        // reset loaded plugins
107f8369d7dSTobias Sarnowski        global $plugin_controller_class, $plugin_controller;
1083a7140a1SAndreas Gohr        /** @var PluginController $plugin_controller */
109f8369d7dSTobias Sarnowski        $plugin_controller = new $plugin_controller_class();
110f8369d7dSTobias Sarnowski
111f8369d7dSTobias Sarnowski        // disable all non-default plugins
112f8369d7dSTobias Sarnowski        global $default_plugins;
113f8369d7dSTobias Sarnowski        foreach ($plugin_controller->getList() as $plugin) {
114f8369d7dSTobias Sarnowski            if (!in_array($plugin, $default_plugins)) {
115f8369d7dSTobias Sarnowski                if (!$plugin_controller->disable($plugin)) {
116f8369d7dSTobias Sarnowski                    throw new Exception('Could not disable plugin "'.$plugin.'"!');
117f8369d7dSTobias Sarnowski                }
118f8369d7dSTobias Sarnowski            }
119f8369d7dSTobias Sarnowski        }
120f8369d7dSTobias Sarnowski
121f8369d7dSTobias Sarnowski        // disable and enable configured plugins
122f8369d7dSTobias Sarnowski        foreach ($this->pluginsDisabled as $plugin) {
123f8369d7dSTobias Sarnowski            if (!$plugin_controller->disable($plugin)) {
124f8369d7dSTobias Sarnowski                throw new Exception('Could not disable plugin "'.$plugin.'"!');
125f8369d7dSTobias Sarnowski            }
126f8369d7dSTobias Sarnowski        }
127f8369d7dSTobias Sarnowski        foreach ($this->pluginsEnabled as $plugin) {
128f8369d7dSTobias Sarnowski            /*  enable() returns false but works...
129f8369d7dSTobias Sarnowski            if (!$plugin_controller->enable($plugin)) {
130f8369d7dSTobias Sarnowski                throw new Exception('Could not enable plugin "'.$plugin.'"!');
131f8369d7dSTobias Sarnowski            }
132f8369d7dSTobias Sarnowski            */
133f8369d7dSTobias Sarnowski            $plugin_controller->enable($plugin);
134f8369d7dSTobias Sarnowski        }
135f8369d7dSTobias Sarnowski
136f8369d7dSTobias Sarnowski        // reset event handler
137f8369d7dSTobias Sarnowski        global $EVENT_HANDLER;
138e1d9dcc8SAndreas Gohr        $EVENT_HANDLER = new EventHandler();
139f8369d7dSTobias Sarnowski
140f8369d7dSTobias Sarnowski        // reload language
141f8369d7dSTobias Sarnowski        $local = $conf['lang'];
142cbb44eabSAndreas Gohr        Event::createAndTrigger('INIT_LANG_LOAD', $local, 'init_lang', true);
143c01cdb70SChristopher Smith
144c01cdb70SChristopher Smith        global $INPUT;
145ccc4c71cSAndreas Gohr        $INPUT = new \dokuwiki\Input\Input();
146f8369d7dSTobias Sarnowski    }
147db5867f1SAndreas Gohr
148db5867f1SAndreas Gohr    /**
1491c0be3ebSAndreas Gohr     * Reinitialize the data directory for this class run
1501c0be3ebSAndreas Gohr     */
1511c0be3ebSAndreas Gohr    public static function setupDataDir() {
1521c0be3ebSAndreas Gohr        // remove any leftovers from the last run
1531c0be3ebSAndreas Gohr        if(is_dir(DOKU_TMP_DATA)) {
1541c0be3ebSAndreas Gohr            // clear indexer data and cache
1551c0be3ebSAndreas Gohr            idx_get_indexer()->clear();
1561c0be3ebSAndreas Gohr            TestUtils::rdelete(DOKU_TMP_DATA);
1571c0be3ebSAndreas Gohr        }
1581c0be3ebSAndreas Gohr
1591c0be3ebSAndreas Gohr        // populate default dirs
1601c0be3ebSAndreas Gohr        TestUtils::rcopy(TMP_DIR, __DIR__ . '/../data/');
1611c0be3ebSAndreas Gohr    }
1621c0be3ebSAndreas Gohr
1631c0be3ebSAndreas Gohr    /**
1641c0be3ebSAndreas Gohr     * Reinitialize the conf directory for this class run
1651c0be3ebSAndreas Gohr     */
1661c0be3ebSAndreas Gohr    public static function setupConfDir() {
1671c0be3ebSAndreas Gohr        $defaults = [
1681c0be3ebSAndreas Gohr            'acronyms.conf',
1691c0be3ebSAndreas Gohr            'dokuwiki.php',
1701c0be3ebSAndreas Gohr            'entities.conf',
1711c0be3ebSAndreas Gohr            'interwiki.conf',
1721c0be3ebSAndreas Gohr            'license.php',
1731c0be3ebSAndreas Gohr            'manifest.json',
1741c0be3ebSAndreas Gohr            'mediameta.php',
1751c0be3ebSAndreas Gohr            'mime.conf',
1761c0be3ebSAndreas Gohr            'plugins.php',
1771c0be3ebSAndreas Gohr            'plugins.required.php',
1781c0be3ebSAndreas Gohr            'scheme.conf',
1791c0be3ebSAndreas Gohr            'smileys.conf',
1801c0be3ebSAndreas Gohr            'wordblock.conf'
1811c0be3ebSAndreas Gohr        ];
1821c0be3ebSAndreas Gohr
1831c0be3ebSAndreas Gohr        // clear any leftovers
1841c0be3ebSAndreas Gohr        if(is_dir(DOKU_CONF)) {
1851c0be3ebSAndreas Gohr            TestUtils::rdelete(DOKU_CONF);
1861c0be3ebSAndreas Gohr        }
1871c0be3ebSAndreas Gohr        mkdir(DOKU_CONF);
1881c0be3ebSAndreas Gohr
1891c0be3ebSAndreas Gohr        // copy defaults
1901c0be3ebSAndreas Gohr        foreach($defaults as $file) {
1911c0be3ebSAndreas Gohr            copy(DOKU_INC . '/conf/' . $file, DOKU_CONF . $file);
1921c0be3ebSAndreas Gohr        }
1931c0be3ebSAndreas Gohr
1941c0be3ebSAndreas Gohr        // copy test files
1951c0be3ebSAndreas Gohr        TestUtils::rcopy(TMP_DIR, __DIR__ . '/../conf');
1961c0be3ebSAndreas Gohr    }
1971c0be3ebSAndreas Gohr
1981c0be3ebSAndreas Gohr    /**
199d732617bSAndreas Gohr     * Waits until a new second has passed
200d732617bSAndreas Gohr     *
2014af692c2SAndreas Gohr     * This tried to be clever about the passing of time and return early if possible. Unfortunately
202e937d004SSatoshi Sahara     * this never worked reliably for unknown reasons. To avoid flaky tests, this now always simply
2034af692c2SAndreas Gohr     * sleeps for a full second on every call.
204d732617bSAndreas Gohr     *
2054af692c2SAndreas Gohr     * @param bool $init no longer used
206d732617bSAndreas Gohr     * @return int new timestamp
207d732617bSAndreas Gohr     */
208d732617bSAndreas Gohr    protected function waitForTick($init = false) {
2094af692c2SAndreas Gohr        sleep(1);
2104af692c2SAndreas Gohr        return time();
211d732617bSAndreas Gohr    }
212210ff133SAndreas Gohr
213210ff133SAndreas Gohr    /**
214210ff133SAndreas Gohr     * Allow for testing inaccessible methods (private or protected)
215210ff133SAndreas Gohr     *
216210ff133SAndreas Gohr     * This makes it easier to test protected methods without needing to create intermediate
217210ff133SAndreas Gohr     * classes inheriting and changing the access.
218210ff133SAndreas Gohr     *
219210ff133SAndreas Gohr     * @link https://stackoverflow.com/a/8702347/172068
220210ff133SAndreas Gohr     * @param object $obj Object in which to call the method
221210ff133SAndreas Gohr     * @param string $func The method to call
222210ff133SAndreas Gohr     * @param array $args The arguments to call the method with
223210ff133SAndreas Gohr     * @return mixed
224210ff133SAndreas Gohr     * @throws ReflectionException when the given obj/func does not exist
225210ff133SAndreas Gohr     */
226210ff133SAndreas Gohr    protected static function callInaccessibleMethod($obj, $func, array $args) {
227210ff133SAndreas Gohr        $class = new \ReflectionClass($obj);
228210ff133SAndreas Gohr        $method = $class->getMethod($func);
229210ff133SAndreas Gohr        $method->setAccessible(true);
230210ff133SAndreas Gohr        return $method->invokeArgs($obj, $args);
231210ff133SAndreas Gohr    }
232836f6efbSAndreas Gohr
233836f6efbSAndreas Gohr    /**
234836f6efbSAndreas Gohr     * Allow for reading inaccessible properties (private or protected)
235836f6efbSAndreas Gohr     *
236836f6efbSAndreas Gohr     * This makes it easier to check internals of tested objects. This should generally
237836f6efbSAndreas Gohr     * be avoided.
238836f6efbSAndreas Gohr     *
239836f6efbSAndreas Gohr     * @param object $obj Object on which to access the property
240836f6efbSAndreas Gohr     * @param string $prop name of the property to access
241836f6efbSAndreas Gohr     * @return mixed
242836f6efbSAndreas Gohr     * @throws ReflectionException  when the given obj/prop does not exist
243836f6efbSAndreas Gohr     */
244836f6efbSAndreas Gohr    protected static function getInaccessibleProperty($obj, $prop) {
245836f6efbSAndreas Gohr        $class = new \ReflectionClass($obj);
246836f6efbSAndreas Gohr        $property = $class->getProperty($prop);
247836f6efbSAndreas Gohr        $property->setAccessible(true);
248836f6efbSAndreas Gohr        return $property->getValue($obj);
249836f6efbSAndreas Gohr    }
250836f6efbSAndreas Gohr
251836f6efbSAndreas Gohr    /**
252836f6efbSAndreas Gohr     * Allow for reading inaccessible properties (private or protected)
253836f6efbSAndreas Gohr     *
254836f6efbSAndreas Gohr     * This makes it easier to set internals of tested objects. This should generally
255836f6efbSAndreas Gohr     * be avoided.
256836f6efbSAndreas Gohr     *
257836f6efbSAndreas Gohr     * @param object $obj Object on which to access the property
258836f6efbSAndreas Gohr     * @param string $prop name of the property to access
259836f6efbSAndreas Gohr     * @param mixed $value new value to set the property to
260836f6efbSAndreas Gohr     * @return void
261836f6efbSAndreas Gohr     * @throws ReflectionException when the given obj/prop does not exist
262836f6efbSAndreas Gohr     */
263836f6efbSAndreas Gohr    protected static function setInaccessibleProperty($obj, $prop, $value) {
264836f6efbSAndreas Gohr        $class = new \ReflectionClass($obj);
265836f6efbSAndreas Gohr        $property = $class->getProperty($prop);
266836f6efbSAndreas Gohr        $property->setAccessible(true);
267836f6efbSAndreas Gohr        $property->setValue($obj, $value);
268836f6efbSAndreas Gohr    }
269f8369d7dSTobias Sarnowski}
270