xref: /dokuwiki/_test/core/DokuWikiTest.php (revision 4568e613ea04e84a7b6fefcc766b6423f64292cf)
1<?php
2if(!class_exists('PHPUnit_Framework_TestCase')) {
3    /**
4     * phpunit 5/6 compatibility
5     */
6    class PHPUnit_Framework_TestCase extends PHPUnit\Framework\TestCase {
7        /**
8         * setExpectedException is deprecated in PHPUnit 6
9         *
10         * @param string $class
11         * @param null|string $message
12         */
13        public function setExpectedException($class, $message=null) {
14            $this->expectException($class);
15            if(!is_null($message)) {
16                $this->expectExceptionMessage($message);
17            }
18        }
19    }
20}
21
22/**
23 * Helper class to provide basic functionality for tests
24 *
25 * @uses PHPUnit_Framework_TestCase and thus PHPUnit 5.7+ is required
26 */
27abstract class DokuWikiTest extends PHPUnit_Framework_TestCase {
28
29    /**
30     * tests can override this
31     *
32     * @var array plugins to enable for test class
33     */
34    protected $pluginsEnabled = array();
35
36    /**
37     * tests can override this
38     *
39     * @var array plugins to disable for test class
40     */
41    protected $pluginsDisabled = array();
42
43    /**
44     * Setup the data directory
45     *
46     * This is ran before each test class
47     */
48    public static function setUpBeforeClass() {
49        // just to be safe not to delete something undefined later
50        if(!defined('TMP_DIR')) die('no temporary directory');
51        if(!defined('DOKU_TMP_DATA')) die('no temporary data directory');
52
53        self::setupDataDir();
54        self::setupConfDir();
55    }
56
57    /**
58     * Reset the DokuWiki environment before each test run. Makes sure loaded config,
59     * language and plugins are correct.
60     *
61     * @throws Exception if plugin actions fail
62     * @return void
63     */
64    public function setUp() {
65
66        // reload config
67        global $conf, $config_cascade;
68        $conf = array();
69        foreach (array('default','local','protected') as $config_group) {
70            if (empty($config_cascade['main'][$config_group])) continue;
71            foreach ($config_cascade['main'][$config_group] as $config_file) {
72                if (file_exists($config_file)) {
73                    include($config_file);
74                }
75            }
76        }
77
78        // reload license config
79        global $license;
80        $license = array();
81
82        // load the license file(s)
83        foreach (array('default','local') as $config_group) {
84            if (empty($config_cascade['license'][$config_group])) continue;
85            foreach ($config_cascade['license'][$config_group] as $config_file) {
86                if(file_exists($config_file)){
87                    include($config_file);
88                }
89            }
90        }
91        // reload some settings
92        $conf['gzip_output'] &= (strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false);
93
94        if($conf['compression'] == 'bz2' && !DOKU_HAS_BZIP) {
95            $conf['compression'] = 'gz';
96        }
97        if($conf['compression'] == 'gz' && !DOKU_HAS_GZIP) {
98            $conf['compression'] = 0;
99        }
100        // make real paths and check them
101        init_paths();
102        init_files();
103
104        // reset loaded plugins
105        global $plugin_controller_class, $plugin_controller;
106        /** @var Doku_Plugin_Controller $plugin_controller */
107        $plugin_controller = new $plugin_controller_class();
108
109        // disable all non-default plugins
110        global $default_plugins;
111        foreach ($plugin_controller->getList() as $plugin) {
112            if (!in_array($plugin, $default_plugins)) {
113                if (!$plugin_controller->disable($plugin)) {
114                    throw new Exception('Could not disable plugin "'.$plugin.'"!');
115                }
116            }
117        }
118
119        // disable and enable configured plugins
120        foreach ($this->pluginsDisabled as $plugin) {
121            if (!$plugin_controller->disable($plugin)) {
122                throw new Exception('Could not disable plugin "'.$plugin.'"!');
123            }
124        }
125        foreach ($this->pluginsEnabled as $plugin) {
126            /*  enable() returns false but works...
127            if (!$plugin_controller->enable($plugin)) {
128                throw new Exception('Could not enable plugin "'.$plugin.'"!');
129            }
130            */
131            $plugin_controller->enable($plugin);
132        }
133
134        // reset event handler
135        global $EVENT_HANDLER;
136        $EVENT_HANDLER = new Doku_Event_Handler();
137
138        // reload language
139        $local = $conf['lang'];
140        trigger_event('INIT_LANG_LOAD', $local, 'init_lang', true);
141
142        global $INPUT;
143        $INPUT = new Input();
144    }
145
146    /**
147     * Reinitialize the data directory for this class run
148     */
149    public static function setupDataDir() {
150        // remove any leftovers from the last run
151        if(is_dir(DOKU_TMP_DATA)) {
152            // clear indexer data and cache
153            idx_get_indexer()->clear();
154            TestUtils::rdelete(DOKU_TMP_DATA);
155        }
156
157        // populate default dirs
158        TestUtils::rcopy(TMP_DIR, __DIR__ . '/../data/');
159    }
160
161    /**
162     * Reinitialize the conf directory for this class run
163     */
164    public static function setupConfDir() {
165        $defaults = [
166            'acronyms.conf',
167            'dokuwiki.php',
168            'entities.conf',
169            'interwiki.conf',
170            'license.php',
171            'manifest.json',
172            'mediameta.php',
173            'mime.conf',
174            'plugins.php',
175            'plugins.required.php',
176            'scheme.conf',
177            'smileys.conf',
178            'wordblock.conf'
179        ];
180
181        // clear any leftovers
182        if(is_dir(DOKU_CONF)) {
183            TestUtils::rdelete(DOKU_CONF);
184        }
185        mkdir(DOKU_CONF);
186
187        // copy defaults
188        foreach($defaults as $file) {
189            copy(DOKU_INC . '/conf/' . $file, DOKU_CONF . $file);
190        }
191
192        // copy test files
193        TestUtils::rcopy(TMP_DIR, __DIR__ . '/../conf');
194    }
195
196    /**
197     * Waits until a new second has passed
198     *
199     * The very first call will return immeadiately, proceeding calls will return
200     * only after at least 1 second after the last call has passed.
201     *
202     * When passing $init=true it will not return immeadiately but use the current
203     * second as initialization. It might still return faster than a second.
204     *
205     * @param bool $init wait from now on, not from last time
206     * @return int new timestamp
207     */
208    protected function waitForTick($init = false) {
209        static $last = 0;
210        if($init) $last = time();
211        while($last === $now = time()) {
212            usleep(100000); //recheck in a 10th of a second
213        }
214        $last = $now;
215        return $now;
216    }
217
218    /**
219     * Allow for testing inaccessible methods (private or protected)
220     *
221     * This makes it easier to test protected methods without needing to create intermediate
222     * classes inheriting and changing the access.
223     *
224     * @link https://stackoverflow.com/a/8702347/172068
225     * @param object $obj Object in which to call the method
226     * @param string $func The method to call
227     * @param array $args The arguments to call the method with
228     * @return mixed
229     * @throws ReflectionException when the given obj/func does not exist
230     */
231    protected static function callInaccessibleMethod($obj, $func, array $args) {
232        $class = new \ReflectionClass($obj);
233        $method = $class->getMethod($func);
234        $method->setAccessible(true);
235        return $method->invokeArgs($obj, $args);
236    }
237}
238