xref: /dokuwiki/_test/core/DokuWikiTest.php (revision bb38a8844779053f301e1eafe5a5214ac3bbb407)
1f8369d7dSTobias Sarnowski<?php
2e1d9dcc8SAndreas Gohr
33a7140a1SAndreas Gohruse dokuwiki\Extension\PluginController;
4cbb44eabSAndreas Gohruse dokuwiki\Extension\Event;
5e1d9dcc8SAndreas Gohruse dokuwiki\Extension\EventHandler;
6*bb38a884SSatoshi Saharause dokuwiki\Search\PageIndex;
7e1d9dcc8SAndreas Gohr
801ef6ea2SAndreas Gohrif (!class_exists('PHPUnit_Framework_TestCase')) {
901ef6ea2SAndreas Gohr    /**
1001ef6ea2SAndreas Gohr     * phpunit 5/6 compatibility
1101ef6ea2SAndreas Gohr     */
12*bb38a884SSatoshi Sahara    class PHPUnit_Framework_TestCase extends PHPUnit\Framework\TestCase
13*bb38a884SSatoshi Sahara    {
1401ef6ea2SAndreas Gohr        /**
153c1490b3SPhy         * setExpectedException is deprecated in PHPUnit 6
163c1490b3SPhy         *
1701ef6ea2SAndreas Gohr         * @param string $class
1801ef6ea2SAndreas Gohr         * @param null|string $message
1901ef6ea2SAndreas Gohr         */
20*bb38a884SSatoshi Sahara        public function setExpectedException($class, $message=null)
21*bb38a884SSatoshi Sahara        {
2201ef6ea2SAndreas Gohr            $this->expectException($class);
2301ef6ea2SAndreas Gohr            if (!is_null($message)) {
2401ef6ea2SAndreas Gohr                $this->expectExceptionMessage($message);
2501ef6ea2SAndreas Gohr            }
2601ef6ea2SAndreas Gohr        }
2701ef6ea2SAndreas Gohr    }
2801ef6ea2SAndreas Gohr}
2901ef6ea2SAndreas Gohr
30f8369d7dSTobias Sarnowski/**
31f8369d7dSTobias Sarnowski * Helper class to provide basic functionality for tests
323c1490b3SPhy *
333c1490b3SPhy * @uses PHPUnit_Framework_TestCase and thus PHPUnit 5.7+ is required
34f8369d7dSTobias Sarnowski */
35*bb38a884SSatoshi Saharaabstract class DokuWikiTest extends PHPUnit_Framework_TestCase
36*bb38a884SSatoshi Sahara{
37f8369d7dSTobias Sarnowski    /**
38f8369d7dSTobias Sarnowski     * tests can override this
39f8369d7dSTobias Sarnowski     *
40f8369d7dSTobias Sarnowski     * @var array plugins to enable for test class
41f8369d7dSTobias Sarnowski     */
42f8369d7dSTobias Sarnowski    protected $pluginsEnabled = array();
43f8369d7dSTobias Sarnowski
44f8369d7dSTobias Sarnowski    /**
45f8369d7dSTobias Sarnowski     * tests can override this
46f8369d7dSTobias Sarnowski     *
47f8369d7dSTobias Sarnowski     * @var array plugins to disable for test class
48f8369d7dSTobias Sarnowski     */
49f8369d7dSTobias Sarnowski    protected $pluginsDisabled = array();
50f8369d7dSTobias Sarnowski
51f8369d7dSTobias Sarnowski    /**
520644090aSAndreas Gohr     * Setup the data directory
530644090aSAndreas Gohr     *
540644090aSAndreas Gohr     * This is ran before each test class
550644090aSAndreas Gohr     */
56*bb38a884SSatoshi Sahara    public static function setUpBeforeClass()
57*bb38a884SSatoshi Sahara    {
580644090aSAndreas Gohr        // just to be safe not to delete something undefined later
590644090aSAndreas Gohr        if(!defined('TMP_DIR')) die('no temporary directory');
600644090aSAndreas Gohr        if(!defined('DOKU_TMP_DATA')) die('no temporary data directory');
610644090aSAndreas Gohr
621c0be3ebSAndreas Gohr        self::setupDataDir();
631c0be3ebSAndreas Gohr        self::setupConfDir();
640644090aSAndreas Gohr    }
650644090aSAndreas Gohr
660644090aSAndreas Gohr    /**
67f8369d7dSTobias Sarnowski     * Reset the DokuWiki environment before each test run. Makes sure loaded config,
68f8369d7dSTobias Sarnowski     * language and plugins are correct.
69f8369d7dSTobias Sarnowski     *
70f8369d7dSTobias Sarnowski     * @throws Exception if plugin actions fail
71f8369d7dSTobias Sarnowski     * @return void
72f8369d7dSTobias Sarnowski     */
73*bb38a884SSatoshi Sahara    public function setUp()
74*bb38a884SSatoshi Sahara    {
75f8369d7dSTobias Sarnowski        // reload config
76f8369d7dSTobias Sarnowski        global $conf, $config_cascade;
77f8369d7dSTobias Sarnowski        $conf = array();
78f8369d7dSTobias Sarnowski        foreach (array('default','local','protected') as $config_group) {
79f8369d7dSTobias Sarnowski            if (empty($config_cascade['main'][$config_group])) continue;
80f8369d7dSTobias Sarnowski            foreach ($config_cascade['main'][$config_group] as $config_file) {
8179e79377SAndreas Gohr                if (file_exists($config_file)) {
82f8369d7dSTobias Sarnowski                    include($config_file);
83f8369d7dSTobias Sarnowski                }
84f8369d7dSTobias Sarnowski            }
85f8369d7dSTobias Sarnowski        }
86f8369d7dSTobias Sarnowski
87f8369d7dSTobias Sarnowski        // reload license config
88f8369d7dSTobias Sarnowski        global $license;
89f8369d7dSTobias Sarnowski        $license = array();
90f8369d7dSTobias Sarnowski
91f8369d7dSTobias Sarnowski        // load the license file(s)
92f8369d7dSTobias Sarnowski        foreach (array('default','local') as $config_group) {
93f8369d7dSTobias Sarnowski            if (empty($config_cascade['license'][$config_group])) continue;
94f8369d7dSTobias Sarnowski            foreach ($config_cascade['license'][$config_group] as $config_file) {
9579e79377SAndreas Gohr                if (file_exists($config_file)) {
96f8369d7dSTobias Sarnowski                    include($config_file);
97f8369d7dSTobias Sarnowski                }
98f8369d7dSTobias Sarnowski            }
99f8369d7dSTobias Sarnowski        }
100f48e16abSGerrit Uitslag        // reload some settings
101f48e16abSGerrit Uitslag        $conf['gzip_output'] &= (strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false);
102f8369d7dSTobias Sarnowski
103f48e16abSGerrit Uitslag        if ($conf['compression'] == 'bz2' && !DOKU_HAS_BZIP) {
104f48e16abSGerrit Uitslag            $conf['compression'] = 'gz';
105f48e16abSGerrit Uitslag        }
106f48e16abSGerrit Uitslag        if ($conf['compression'] == 'gz' && !DOKU_HAS_GZIP) {
107f48e16abSGerrit Uitslag            $conf['compression'] = 0;
108f48e16abSGerrit Uitslag        }
109f8369d7dSTobias Sarnowski        // make real paths and check them
110f8369d7dSTobias Sarnowski        init_paths();
111f8369d7dSTobias Sarnowski        init_files();
112f8369d7dSTobias Sarnowski
113f8369d7dSTobias Sarnowski        // reset loaded plugins
114f8369d7dSTobias Sarnowski        global $plugin_controller_class, $plugin_controller;
1153a7140a1SAndreas Gohr        /** @var PluginController $plugin_controller */
116f8369d7dSTobias Sarnowski        $plugin_controller = new $plugin_controller_class();
117f8369d7dSTobias Sarnowski
118f8369d7dSTobias Sarnowski        // disable all non-default plugins
119f8369d7dSTobias Sarnowski        global $default_plugins;
120f8369d7dSTobias Sarnowski        foreach ($plugin_controller->getList() as $plugin) {
121f8369d7dSTobias Sarnowski            if (!in_array($plugin, $default_plugins)) {
122f8369d7dSTobias Sarnowski                if (!$plugin_controller->disable($plugin)) {
123f8369d7dSTobias Sarnowski                    throw new Exception('Could not disable plugin "'.$plugin.'"!');
124f8369d7dSTobias Sarnowski                }
125f8369d7dSTobias Sarnowski            }
126f8369d7dSTobias Sarnowski        }
127f8369d7dSTobias Sarnowski
128f8369d7dSTobias Sarnowski        // disable and enable configured plugins
129f8369d7dSTobias Sarnowski        foreach ($this->pluginsDisabled as $plugin) {
130f8369d7dSTobias Sarnowski            if (!$plugin_controller->disable($plugin)) {
131f8369d7dSTobias Sarnowski                throw new Exception('Could not disable plugin "'.$plugin.'"!');
132f8369d7dSTobias Sarnowski            }
133f8369d7dSTobias Sarnowski        }
134f8369d7dSTobias Sarnowski        foreach ($this->pluginsEnabled as $plugin) {
135f8369d7dSTobias Sarnowski            /*  enable() returns false but works...
136f8369d7dSTobias Sarnowski            if (!$plugin_controller->enable($plugin)) {
137f8369d7dSTobias Sarnowski                throw new Exception('Could not enable plugin "'.$plugin.'"!');
138f8369d7dSTobias Sarnowski            }
139f8369d7dSTobias Sarnowski            */
140f8369d7dSTobias Sarnowski            $plugin_controller->enable($plugin);
141f8369d7dSTobias Sarnowski        }
142f8369d7dSTobias Sarnowski
143f8369d7dSTobias Sarnowski        // reset event handler
144f8369d7dSTobias Sarnowski        global $EVENT_HANDLER;
145e1d9dcc8SAndreas Gohr        $EVENT_HANDLER = new EventHandler();
146f8369d7dSTobias Sarnowski
147f8369d7dSTobias Sarnowski        // reload language
148f8369d7dSTobias Sarnowski        $local = $conf['lang'];
149cbb44eabSAndreas Gohr        Event::createAndTrigger('INIT_LANG_LOAD', $local, 'init_lang', true);
150c01cdb70SChristopher Smith
151c01cdb70SChristopher Smith        global $INPUT;
152ccc4c71cSAndreas Gohr        $INPUT = new \dokuwiki\Input\Input();
153f8369d7dSTobias Sarnowski    }
154db5867f1SAndreas Gohr
155db5867f1SAndreas Gohr    /**
1561c0be3ebSAndreas Gohr     * Reinitialize the data directory for this class run
1571c0be3ebSAndreas Gohr     */
158*bb38a884SSatoshi Sahara    public static function setupDataDir()
159*bb38a884SSatoshi Sahara    {
1601c0be3ebSAndreas Gohr        // remove any leftovers from the last run
1611c0be3ebSAndreas Gohr        if(is_dir(DOKU_TMP_DATA)) {
1621c0be3ebSAndreas Gohr            // clear indexer data and cache
163*bb38a884SSatoshi Sahara            $PageIndex = PageIndex::getInstance();
164*bb38a884SSatoshi Sahara            $PageIndex->clear();
1651c0be3ebSAndreas Gohr            TestUtils::rdelete(DOKU_TMP_DATA);
1661c0be3ebSAndreas Gohr        }
1671c0be3ebSAndreas Gohr
1681c0be3ebSAndreas Gohr        // populate default dirs
1691c0be3ebSAndreas Gohr        TestUtils::rcopy(TMP_DIR, __DIR__ . '/../data/');
1701c0be3ebSAndreas Gohr    }
1711c0be3ebSAndreas Gohr
1721c0be3ebSAndreas Gohr    /**
1731c0be3ebSAndreas Gohr     * Reinitialize the conf directory for this class run
1741c0be3ebSAndreas Gohr     */
175*bb38a884SSatoshi Sahara    public static function setupConfDir()
176*bb38a884SSatoshi Sahara    {
1771c0be3ebSAndreas Gohr        $defaults = [
1781c0be3ebSAndreas Gohr            'acronyms.conf',
1791c0be3ebSAndreas Gohr            'dokuwiki.php',
1801c0be3ebSAndreas Gohr            'entities.conf',
1811c0be3ebSAndreas Gohr            'interwiki.conf',
1821c0be3ebSAndreas Gohr            'license.php',
1831c0be3ebSAndreas Gohr            'manifest.json',
1841c0be3ebSAndreas Gohr            'mediameta.php',
1851c0be3ebSAndreas Gohr            'mime.conf',
1861c0be3ebSAndreas Gohr            'plugins.php',
1871c0be3ebSAndreas Gohr            'plugins.required.php',
1881c0be3ebSAndreas Gohr            'scheme.conf',
1891c0be3ebSAndreas Gohr            'smileys.conf',
1901c0be3ebSAndreas Gohr            'wordblock.conf'
1911c0be3ebSAndreas Gohr        ];
1921c0be3ebSAndreas Gohr
1931c0be3ebSAndreas Gohr        // clear any leftovers
1941c0be3ebSAndreas Gohr        if (is_dir(DOKU_CONF)) {
1951c0be3ebSAndreas Gohr            TestUtils::rdelete(DOKU_CONF);
1961c0be3ebSAndreas Gohr        }
1971c0be3ebSAndreas Gohr        mkdir(DOKU_CONF);
1981c0be3ebSAndreas Gohr
1991c0be3ebSAndreas Gohr        // copy defaults
2001c0be3ebSAndreas Gohr        foreach ($defaults as $file) {
2011c0be3ebSAndreas Gohr            copy(DOKU_INC . '/conf/' . $file, DOKU_CONF . $file);
2021c0be3ebSAndreas Gohr        }
2031c0be3ebSAndreas Gohr
2041c0be3ebSAndreas Gohr        // copy test files
2051c0be3ebSAndreas Gohr        TestUtils::rcopy(TMP_DIR, __DIR__ . '/../conf');
2061c0be3ebSAndreas Gohr    }
2071c0be3ebSAndreas Gohr
2081c0be3ebSAndreas Gohr    /**
209d732617bSAndreas Gohr     * Waits until a new second has passed
210d732617bSAndreas Gohr     *
2114af692c2SAndreas Gohr     * This tried to be clever about the passing of time and return early if possible. Unfortunately
2124af692c2SAndreas Gohr     * this never worked reliably fo unknown reasons. To avoid flaky tests, this now always simply
2134af692c2SAndreas Gohr     * sleeps for a full second on every call.
214d732617bSAndreas Gohr     *
2154af692c2SAndreas Gohr     * @param bool $init no longer used
216d732617bSAndreas Gohr     * @return int new timestamp
217d732617bSAndreas Gohr     */
218*bb38a884SSatoshi Sahara    protected function waitForTick($init = false)
219*bb38a884SSatoshi Sahara    {
2204af692c2SAndreas Gohr        sleep(1);
2214af692c2SAndreas Gohr        return time();
222d732617bSAndreas Gohr    }
223210ff133SAndreas Gohr
224210ff133SAndreas Gohr    /**
225210ff133SAndreas Gohr     * Allow for testing inaccessible methods (private or protected)
226210ff133SAndreas Gohr     *
227210ff133SAndreas Gohr     * This makes it easier to test protected methods without needing to create intermediate
228210ff133SAndreas Gohr     * classes inheriting and changing the access.
229210ff133SAndreas Gohr     *
230210ff133SAndreas Gohr     * @link https://stackoverflow.com/a/8702347/172068
231210ff133SAndreas Gohr     * @param object $obj Object in which to call the method
232210ff133SAndreas Gohr     * @param string $func The method to call
233210ff133SAndreas Gohr     * @param array $args The arguments to call the method with
234210ff133SAndreas Gohr     * @return mixed
235210ff133SAndreas Gohr     * @throws ReflectionException when the given obj/func does not exist
236210ff133SAndreas Gohr     */
237*bb38a884SSatoshi Sahara    protected static function callInaccessibleMethod($obj, $func, array $args)
238*bb38a884SSatoshi Sahara    {
239210ff133SAndreas Gohr        $class = new \ReflectionClass($obj);
240210ff133SAndreas Gohr        $method = $class->getMethod($func);
241210ff133SAndreas Gohr        $method->setAccessible(true);
242210ff133SAndreas Gohr        return $method->invokeArgs($obj, $args);
243210ff133SAndreas Gohr    }
244836f6efbSAndreas Gohr
245836f6efbSAndreas Gohr    /**
246836f6efbSAndreas Gohr     * Allow for reading inaccessible properties (private or protected)
247836f6efbSAndreas Gohr     *
248836f6efbSAndreas Gohr     * This makes it easier to check internals of tested objects. This should generally
249836f6efbSAndreas Gohr     * be avoided.
250836f6efbSAndreas Gohr     *
251836f6efbSAndreas Gohr     * @param object $obj Object on which to access the property
252836f6efbSAndreas Gohr     * @param string $prop name of the property to access
253836f6efbSAndreas Gohr     * @return mixed
254836f6efbSAndreas Gohr     * @throws ReflectionException  when the given obj/prop does not exist
255836f6efbSAndreas Gohr     */
256*bb38a884SSatoshi Sahara    protected static function getInaccessibleProperty($obj, $prop)
257*bb38a884SSatoshi Sahara    {
258836f6efbSAndreas Gohr        $class = new \ReflectionClass($obj);
259836f6efbSAndreas Gohr        $property = $class->getProperty($prop);
260836f6efbSAndreas Gohr        $property->setAccessible(true);
261836f6efbSAndreas Gohr        return $property->getValue($obj);
262836f6efbSAndreas Gohr    }
263836f6efbSAndreas Gohr
264836f6efbSAndreas Gohr    /**
265836f6efbSAndreas Gohr     * Allow for reading inaccessible properties (private or protected)
266836f6efbSAndreas Gohr     *
267836f6efbSAndreas Gohr     * This makes it easier to set internals of tested objects. This should generally
268836f6efbSAndreas Gohr     * be avoided.
269836f6efbSAndreas Gohr     *
270836f6efbSAndreas Gohr     * @param object $obj Object on which to access the property
271836f6efbSAndreas Gohr     * @param string $prop name of the property to access
272836f6efbSAndreas Gohr     * @param mixed $value new value to set the property to
273836f6efbSAndreas Gohr     * @return void
274836f6efbSAndreas Gohr     * @throws ReflectionException when the given obj/prop does not exist
275836f6efbSAndreas Gohr     */
276*bb38a884SSatoshi Sahara    protected static function setInaccessibleProperty($obj, $prop, $value)
277*bb38a884SSatoshi Sahara    {
278836f6efbSAndreas Gohr        $class = new \ReflectionClass($obj);
279836f6efbSAndreas Gohr        $property = $class->getProperty($prop);
280836f6efbSAndreas Gohr        $property->setAccessible(true);
281836f6efbSAndreas Gohr        $property->setValue($obj, $value);
282836f6efbSAndreas Gohr    }
283f8369d7dSTobias Sarnowski}
284