aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/_test/core/DokuWikiTest.php
blob: 5bafd87579e02f1fe129dc6e9106a2cfec795341 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
<?php

use dokuwiki\Extension\PluginController;
use dokuwiki\Extension\Event;
use dokuwiki\Extension\EventHandler;
use dokuwiki\Logger;

/**
 * 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);
    }

    /**
     * Expect the next log message to contain $message
     *
     * @param string $facility
     * @param string $message
     * @return void
     */
    protected function expectLogMessage(string $message, string $facility = Logger::LOG_ERROR): void
    {
        $logger = Logger::getInstance($facility);
        $logger->expect($message);
    }
}