2 // This file is part of Moodle - http://moodle.org/
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Unit tests for setuplib.php
22 * @copyright 2012 The Open University
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
29 * Unit tests for setuplib.php
31 * @copyright 2012 The Open University
32 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34 class core_setuplib_testcase
extends advanced_testcase
{
37 * Test get_docs_url_standard in the normal case when we should link to Moodle docs.
39 public function test_get_docs_url_standard() {
41 if (empty($CFG->docroot
)) {
42 $docroot = 'http://docs.moodle.org/';
44 $docroot = $CFG->docroot
;
46 $this->assertRegExp('~^' . preg_quote($docroot, '') . '/\d{2}/' . current_language() . '/course/editing$~',
47 get_docs_url('course/editing'));
51 * Test get_docs_url_standard in the special case of an absolute HTTP URL.
53 public function test_get_docs_url_http() {
54 $url = 'http://moodle.org/';
55 $this->assertEquals($url, get_docs_url($url));
59 * Test get_docs_url_standard in the special case of an absolute HTTPS URL.
61 public function test_get_docs_url_https() {
62 $url = 'https://moodle.org/';
63 $this->assertEquals($url, get_docs_url($url));
67 * Test get_docs_url_standard in the special case of a link relative to wwwroot.
69 public function test_get_docs_url_wwwroot() {
71 $this->assertSame($CFG->wwwroot
. '/lib/tests/setuplib_test.php',
72 get_docs_url('%%WWWROOT%%/lib/tests/setuplib_test.php'));
76 * Test if get_exception_info() removes file system paths.
78 public function test_exception_info_removes_serverpaths() {
81 // This doesn't test them all possible ones, but these are set for unit tests.
82 $cfgnames = array('dataroot', 'dirroot', 'tempdir', 'backuptempdir', 'cachedir', 'localcachedir');
86 foreach ($cfgnames as $cfgname) {
87 if (!empty($CFG->$cfgname)) {
88 $fixture .= $CFG->$cfgname.' ';
89 $expected .= "[$cfgname] ";
92 $exception = new moodle_exception('generalexceptionmessage', 'error', '', $fixture, $fixture);
93 $exceptioninfo = get_exception_info($exception);
95 $this->assertContains($expected, $exceptioninfo->message
, 'Exception message does not contain system paths');
96 $this->assertContains($expected, $exceptioninfo->debuginfo
, 'Exception debug info does not contain system paths');
99 public function test_localcachedir() {
102 $this->resetAfterTest(true);
104 // Test default location - can not be modified in phpunit tests because we override everything in config.php.
105 $this->assertSame("$CFG->dataroot/localcache", $CFG->localcachedir
);
107 $this->setCurrentTimeStart();
108 $timestampfile = "$CFG->localcachedir/.lastpurged";
110 // Delete existing localcache directory, as this is testing first call
111 // to make_localcache_directory.
112 remove_dir($CFG->localcachedir
, true);
113 $dir = make_localcache_directory('', false);
114 $this->assertSame($CFG->localcachedir
, $dir);
115 $this->assertFileNotExists("$CFG->localcachedir/.htaccess");
116 $this->assertFileExists($timestampfile);
117 $this->assertTimeCurrent(filemtime($timestampfile));
119 $dir = make_localcache_directory('test/test', false);
120 $this->assertSame("$CFG->localcachedir/test/test", $dir);
122 // Test custom location.
123 $CFG->localcachedir
= "$CFG->dataroot/testlocalcache";
124 $this->setCurrentTimeStart();
125 $timestampfile = "$CFG->localcachedir/.lastpurged";
126 $this->assertFileNotExists($timestampfile);
128 $dir = make_localcache_directory('', false);
129 $this->assertSame($CFG->localcachedir
, $dir);
130 $this->assertFileExists("$CFG->localcachedir/.htaccess");
131 $this->assertFileExists($timestampfile);
132 $this->assertTimeCurrent(filemtime($timestampfile));
134 $dir = make_localcache_directory('test', false);
135 $this->assertSame("$CFG->localcachedir/test", $dir);
137 $prevtime = filemtime($timestampfile);
138 $dir = make_localcache_directory('pokus', false);
139 $this->assertSame("$CFG->localcachedir/pokus", $dir);
140 $this->assertSame($prevtime, filemtime($timestampfile));
143 $testfile = "$CFG->localcachedir/test/test.txt";
144 $this->assertTrue(touch($testfile));
146 $now = $this->setCurrentTimeStart();
147 set_config('localcachedirpurged', $now - 2);
149 $this->assertFileNotExists($testfile);
150 $this->assertFileNotExists(dirname($testfile));
151 $this->assertFileExists($timestampfile);
152 $this->assertTimeCurrent(filemtime($timestampfile));
153 $this->assertTimeCurrent($CFG->localcachedirpurged
);
155 // Simulates purge_all_caches() on another server node.
156 make_localcache_directory('test', false);
157 $this->assertTrue(touch($testfile));
158 set_config('localcachedirpurged', $now - 1);
159 $this->assertTrue(touch($timestampfile, $now - 2));
161 $this->assertSame($now - 2, filemtime($timestampfile));
163 $this->setCurrentTimeStart();
164 $dir = make_localcache_directory('', false);
165 $this->assertSame("$CFG->localcachedir", $dir);
166 $this->assertFileNotExists($testfile);
167 $this->assertFileNotExists(dirname($testfile));
168 $this->assertFileExists($timestampfile);
169 $this->assertTimeCurrent(filemtime($timestampfile));
172 public function test_make_unique_directory_basedir_is_file() {
175 // Start with a file instead of a directory.
176 $base = $CFG->tempdir
. DIRECTORY_SEPARATOR
. md5(microtime(true) +
rand());
179 // First the false test.
180 $this->assertFalse(make_unique_writable_directory($base, false));
182 // Now check for exception.
183 $this->expectException('invalid_dataroot_permissions');
184 $this->expectExceptionMessage($base . ' is not writable. Unable to create a unique directory within it.');
185 make_unique_writable_directory($base);
190 public function test_make_unique_directory() {
193 // Create directories should be both directories, and writable.
194 $firstdir = make_unique_writable_directory($CFG->tempdir
);
195 $this->assertTrue(is_dir($firstdir));
196 $this->assertTrue(is_writable($firstdir));
198 $seconddir = make_unique_writable_directory($CFG->tempdir
);
199 $this->assertTrue(is_dir($seconddir));
200 $this->assertTrue(is_writable($seconddir));
202 // Directories should be different each iteration.
203 $this->assertNotEquals($firstdir, $seconddir);
206 public function test_get_request_storage_directory() {
207 // Making a call to get_request_storage_directory should always give the same result.
208 $firstdir = get_request_storage_directory();
209 $seconddir = get_request_storage_directory();
210 $this->assertTrue(is_dir($firstdir));
211 $this->assertEquals($firstdir, $seconddir);
213 // Removing the directory and calling get_request_storage_directory() again should cause a new directory to be created.
214 remove_dir($firstdir);
215 $this->assertFalse(file_exists($firstdir));
216 $this->assertFalse(is_dir($firstdir));
218 $thirddir = get_request_storage_directory();
219 $this->assertTrue(is_dir($thirddir));
220 $this->assertNotEquals($firstdir, $thirddir);
222 // Removing it and replacing it with a file should cause it to be regenerated again.
223 remove_dir($thirddir);
224 $this->assertFalse(file_exists($thirddir));
225 $this->assertFalse(is_dir($thirddir));
227 $this->assertTrue(file_exists($thirddir));
228 $this->assertFalse(is_dir($thirddir));
230 $fourthdir = get_request_storage_directory();
231 $this->assertTrue(is_dir($fourthdir));
232 $this->assertNotEquals($thirddir, $fourthdir);
236 public function test_make_request_directory() {
237 // Every request directory should be unique.
238 $firstdir = make_request_directory();
239 $seconddir = make_request_directory();
240 $thirddir = make_request_directory();
241 $fourthdir = make_request_directory();
243 $this->assertNotEquals($firstdir, $seconddir);
244 $this->assertNotEquals($firstdir, $thirddir);
245 $this->assertNotEquals($firstdir, $fourthdir);
246 $this->assertNotEquals($seconddir, $thirddir);
247 $this->assertNotEquals($seconddir, $fourthdir);
248 $this->assertNotEquals($thirddir, $fourthdir);
250 // They should also all be within the request storage directory.
251 $requestdir = get_request_storage_directory();
252 $this->assertEquals(0, strpos($firstdir, $requestdir));
253 $this->assertEquals(0, strpos($seconddir, $requestdir));
254 $this->assertEquals(0, strpos($thirddir, $requestdir));
255 $this->assertEquals(0, strpos($fourthdir, $requestdir));
257 // Removing the requestdir should mean that new request directories are still created successfully.
258 remove_dir($requestdir);
259 $this->assertFalse(file_exists($requestdir));
260 $this->assertFalse(is_dir($requestdir));
262 $fifthdir = make_request_directory();
263 $this->assertNotEquals($firstdir, $fifthdir);
264 $this->assertNotEquals($seconddir, $fifthdir);
265 $this->assertNotEquals($thirddir, $fifthdir);
266 $this->assertNotEquals($fourthdir, $fifthdir);
267 $this->assertTrue(is_dir($fifthdir));
268 $this->assertFalse(strpos($fifthdir, $requestdir));
270 // And it should be within the new request directory.
271 $newrequestdir = get_request_storage_directory();
272 $this->assertEquals(0, strpos($fifthdir, $newrequestdir));
275 public function test_merge_query_params() {
279 'action' => 'delete',
292 'numerical' => array(
293 '2' => array('a' => 'b'),
299 'numerical' => array(
301 '2' => array('d' => 'e'),
303 'action' => 'create',
316 'action' => 'create',
330 'numerical' => array(
331 '2' => array('a' => 'b', 'd' => 'e'),
339 merge_query_params($array, $chunk);
341 $this->assertSame($expected, $array);
342 $this->assertNotSame($original, $array);
344 $query = "id=1&course=2&action=create&grade%5B%5D=a&grade%5B%5D=b&grade%5B%5D=c&grade%5B%5D=e&grade%5B%5D=f&grade%5B%5D=g&items%5Ba%5D=aa&items%5Bb%5D=bb&mix=mix&numerical%5B2%5D%5Ba%5D=b&numerical%5B2%5D%5Bd%5D=e&numerical%5B1%5D=2&numerical%5B0%5D=z&next=2";
346 parse_str($query, $decoded);
347 $this->assertSame($expected, $decoded);
349 // Prove that we cannot use array_merge_recursive() instead.
350 $this->assertNotSame($expected, array_merge_recursive($original, $chunk));
354 * Test the link processed by get_exception_info().
356 public function test_get_exception_info_link() {
357 global $CFG, $SESSION;
359 $httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot
);
362 $url = $CFG->wwwroot
. '/something/here?really=yes';
363 $exception = new moodle_exception('none', 'error', $url);
364 $infos = $this->get_exception_info($exception);
365 $this->assertSame($url, $infos->link
);
367 // Relative local URL.
368 $url = '/something/here?really=yes';
369 $exception = new moodle_exception('none', 'error', $url);
370 $infos = $this->get_exception_info($exception);
371 $this->assertSame($CFG->wwwroot
. '/', $infos->link
);
373 // HTTPS URL when login HTTPS is not enabled (default) and site is HTTP.
374 $CFG->wwwroot
= str_replace('https:', 'http:', $CFG->wwwroot
);
375 $url = $httpswwwroot . '/something/here?really=yes';
376 $exception = new moodle_exception('none', 'error', $url);
377 $infos = $this->get_exception_info($exception);
378 $this->assertSame($CFG->wwwroot
. '/', $infos->link
);
380 // HTTPS URL when login HTTPS is not enabled and site is HTTPS.
381 $CFG->wwwroot
= str_replace('http:', 'https:', $CFG->wwwroot
);
382 $url = $httpswwwroot . '/something/here?really=yes';
383 $exception = new moodle_exception('none', 'error', $url);
384 $infos = $this->get_exception_info($exception);
385 $this->assertSame($url, $infos->link
);
387 // External HTTP URL.
388 $url = 'http://moodle.org/something/here?really=yes';
389 $exception = new moodle_exception('none', 'error', $url);
390 $infos = $this->get_exception_info($exception);
391 $this->assertSame($CFG->wwwroot
. '/', $infos->link
);
393 // External HTTPS URL.
394 $url = 'https://moodle.org/something/here?really=yes';
395 $exception = new moodle_exception('none', 'error', $url);
396 $infos = $this->get_exception_info($exception);
397 $this->assertSame($CFG->wwwroot
. '/', $infos->link
);
399 // External URL containing local URL.
400 $url = 'http://moodle.org/something/here?' . $CFG->wwwroot
;
401 $exception = new moodle_exception('none', 'error', $url);
402 $infos = $this->get_exception_info($exception);
403 $this->assertSame($CFG->wwwroot
. '/', $infos->link
);
405 // Internal link from fromurl.
406 $SESSION->fromurl
= $url = $CFG->wwwroot
. '/something/here?really=yes';
407 $exception = new moodle_exception('none');
408 $infos = $this->get_exception_info($exception);
409 $this->assertSame($url, $infos->link
);
411 // Internal HTTPS link from fromurl.
412 $SESSION->fromurl
= $url = $httpswwwroot . '/something/here?really=yes';
413 $exception = new moodle_exception('none');
414 $infos = $this->get_exception_info($exception);
415 $this->assertSame($url, $infos->link
);
417 // External link from fromurl.
418 $SESSION->fromurl
= 'http://moodle.org/something/here?really=yes';
419 $exception = new moodle_exception('none');
420 $infos = $this->get_exception_info($exception);
421 $this->assertSame($CFG->wwwroot
. '/', $infos->link
);
423 // External HTTPS link from fromurl.
424 $SESSION->fromurl
= 'https://moodle.org/something/here?really=yes';
425 $exception = new moodle_exception('none');
426 $infos = $this->get_exception_info($exception);
427 $this->assertSame($CFG->wwwroot
. '/', $infos->link
);
429 $SESSION->fromurl
= '';
433 * Wrapper to call {@link get_exception_info()}.
435 * @param Exception $ex An exception.
436 * @return stdClass of information.
438 public function get_exception_info($ex) {
441 } catch (moodle_exception
$e) {
442 return get_exception_info($e);
447 * Data provider for test_get_real_size().
449 * @return array An array of arrays contain test data
451 public function data_for_test_get_real_size() {
457 array('50MB', 52428800),
458 array('50Mb', 52428800),
459 array('50M', 52428800),
460 array('50m', 52428800),
461 array('8Gb', 8589934592),
462 array('8GB', 8589934592),
463 array('8G', 8589934592),
468 * Test the get_real_size() function.
470 * @dataProvider data_for_test_get_real_size
472 * @param string $input the input for get_real_size()
473 * @param int $expectedbytes the expected bytes
475 public function test_get_real_size($input, $expectedbytes) {
476 $this->assertEquals($expectedbytes, get_real_size($input));
480 * Validate the given V4 UUID.
482 * @param string $value The candidate V4 UUID
483 * @return bool True if valid; otherwise, false.
485 protected static function is_valid_uuid_v4($value) {
486 // Version 4 UUIDs have the form xxxxxxxx-xxxx-4xxx-Yxxx-xxxxxxxxxxxx
487 // where x is any hexadecimal digit and Y is one of 8, 9, aA, or bB.
488 // First, the size is 36 (32 + 4 dashes).
489 if (strlen($value) != 36) {
492 // Finally, check the format.
493 $uuidv4pattern = '/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i';
494 return (preg_match($uuidv4pattern, $value) === 1);
498 * Test the \core\uuid::generate_uuid_via_pecl_uuid_extension() function.
500 public function test_core_uuid_generate_uuid_via_pecl_uuid_extension() {
501 if (!extension_loaded('uuid')) {
502 $this->markTestSkipped("PHP 'uuid' extension not loaded.");
504 if (!function_exists('uuid_time')) {
505 $this->markTestSkipped("PHP PECL 'uuid' extension not loaded.");
508 // The \core\uuid::generate_uuid_via_pecl_uuid_extension static method is protected. Use Reflection to call the method.
509 $method = new ReflectionMethod('\core\uuid', 'generate_uuid_via_pecl_uuid_extension');
510 $method->setAccessible(true);
511 $uuid = $method->invoke(null);
512 $this->assertTrue(self
::is_valid_uuid_v4($uuid), "Invalid v4 uuid: '$uuid'");
516 * Test the \core\uuid::generate_uuid_via_random_bytes() function.
518 public function test_core_uuid_generate_uuid_via_random_bytes() {
521 } catch (Exception
$e) {
522 $this->markTestSkipped('No source of entropy for random_bytes. ' . $e->getMessage());
525 // The \core\uuid::generate_uuid_via_random_bytes static method is protected. Use Reflection to call the method.
526 $method = new ReflectionMethod('\core\uuid', 'generate_uuid_via_random_bytes');
527 $method->setAccessible(true);
528 $uuid = $method->invoke(null);
529 $this->assertTrue(self
::is_valid_uuid_v4($uuid), "Invalid v4 uuid: '$uuid'");
533 * Test the \core\uuid::generate() function.
535 public function test_core_uuid_generate() {
536 $uuid = \core\uuid
::generate();
537 $this->assertTrue(self
::is_valid_uuid_v4($uuid), "Invalid v4 UUID: '$uuid'");