moodlelib_test.php 215 KB
Newer Older
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
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Unit tests for (some of) ../moodlelib.php.
 *
 * @package    core
 * @category   phpunit
 * @copyright  &copy; 2006 The Open University
 * @author     T.J.Hunt@open.ac.uk
 * @author     nicolas@moodle.com
 */

defined('MOODLE_INTERNAL') || die();

29
class core_moodlelib_testcase extends advanced_testcase {
30
31
32

    public static $includecoverage = array('lib/moodlelib.php');

33
34
35
36
37
38
    /**
     * Define a local decimal separator.
     *
     * It is not possible to directly change the result of get_string in
     * a unit test. Instead, we create a language pack for language 'xx' in
     * dataroot and make langconfig.php with the string we need to change.
39
     * The default example separator used here is 'X'; on PHP 5.3 and before this
40
41
     * must be a single byte character due to PHP bug/limitation in
     * number_format, so you can't use UTF-8 characters.
42
43
     *
     * @param string $decsep Separator character. Defaults to `'X'`.
44
     */
45
    protected function define_local_decimal_separator(string $decsep = 'X') {
46
47
48
        global $SESSION, $CFG;

        $SESSION->lang = 'xx';
49
        $langconfig = "<?php\n\$string['decsep'] = '$decsep';";
50
51
52
        $langfolder = $CFG->dataroot . '/lang/xx';
        check_dir_exists($langfolder);
        file_put_contents($langfolder . '/langconfig.php', $langconfig);
53
54
55
56

        // Ensure the new value is picked up and not taken from the cache.
        $stringmanager = get_string_manager();
        $stringmanager->reset_caches(true);
57
58
    }

59
60
61
62
    public function test_cleanremoteaddr() {
        // IPv4.
        $this->assertNull(cleanremoteaddr('1023.121.234.1'));
        $this->assertSame('123.121.234.1', cleanremoteaddr('123.121.234.01 '));
63

64
65
66
67
68
69
70
71
72
73
74
75
        // IPv6.
        $this->assertNull(cleanremoteaddr('0:0:0:0:0:0:0:0:0'));
        $this->assertNull(cleanremoteaddr('0:0:0:0:0:0:0:abh'));
        $this->assertNull(cleanremoteaddr('0:0:0:::0:0:1'));
        $this->assertSame('::', cleanremoteaddr('0:0:0:0:0:0:0:0', true));
        $this->assertSame('::1:1', cleanremoteaddr('0:0:0:0:0:0:1:1', true));
        $this->assertSame('abcd:ef::', cleanremoteaddr('abcd:00ef:0:0:0:0:0:0', true));
        $this->assertSame('1::1', cleanremoteaddr('1:0:0:0:0:0:0:1', true));
        $this->assertSame('0:0:0:0:0:0:10:1', cleanremoteaddr('::10:1', false));
        $this->assertSame('1:1:0:0:0:0:0:0', cleanremoteaddr('01:1::', false));
        $this->assertSame('10:0:0:0:0:0:0:10', cleanremoteaddr('10::10', false));
        $this->assertSame('::ffff:c0a8:11', cleanremoteaddr('::ffff:192.168.1.1', true));
76
77
    }

78
79
    public function test_address_in_subnet() {
        // 1: xxx.xxx.xxx.xxx/nn or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/nnn (number of bits in net mask).
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
        $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.1/32'));
        $this->assertFalse(address_in_subnet('123.121.23.1', '123.121.23.0/32'));
        $this->assertTrue(address_in_subnet('10.10.10.100',  '123.121.23.45/0'));
        $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/24'));
        $this->assertFalse(address_in_subnet('123.121.34.1', '123.121.234.0/24'));
        $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/30'));
        $this->assertFalse(address_in_subnet('123.121.23.8', '123.121.23.0/30'));
        $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128'));
        $this->assertFalse(address_in_subnet('bab:baba::baba', 'bab:baba::cece/128'));
        $this->assertTrue(address_in_subnet('baba:baba::baba', 'cece:cece::cece/0'));
        $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128'));
        $this->assertTrue(address_in_subnet('baba:baba::00ba', 'baba:baba::/120'));
        $this->assertFalse(address_in_subnet('baba:baba::aba', 'baba:baba::/120'));
        $this->assertTrue(address_in_subnet('baba::baba:00ba', 'baba::baba:0/112'));
        $this->assertFalse(address_in_subnet('baba::aba:00ba', 'baba::baba:0/112'));
        $this->assertFalse(address_in_subnet('aba::baba:0000', 'baba::baba:0/112'));

97
        // Fixed input.
98
99
100
        $this->assertTrue(address_in_subnet('123.121.23.1   ', ' 123.121.23.0 / 24'));
        $this->assertTrue(address_in_subnet('::ffff:10.1.1.1', ' 0:0:0:000:0:ffff:a1:10 / 126'));

101
        // Incorrect input.
102
103
104
105
106
107
108
109
110
111
112
        $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/-2'));
        $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/64'));
        $this->assertFalse(address_in_subnet('123.121.234.x', '123.121.234.1/24'));
        $this->assertFalse(address_in_subnet('123.121.234.0', '123.121.234.xx/24'));
        $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/xx0'));
        $this->assertFalse(address_in_subnet('::1', '::aa:0/xx0'));
        $this->assertFalse(address_in_subnet('::1', '::aa:0/-5'));
        $this->assertFalse(address_in_subnet('::1', '::aa:0/130'));
        $this->assertFalse(address_in_subnet('x:1', '::aa:0/130'));
        $this->assertFalse(address_in_subnet('::1', '::ax:0/130'));

113
        // 2: xxx.xxx.xxx.xxx-yyy or  xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx::xxxx-yyyy (a range of IP addresses in the last group).
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
        $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12-14'));
        $this->assertTrue(address_in_subnet('123.121.234.13', '123.121.234.12-14'));
        $this->assertTrue(address_in_subnet('123.121.234.14', '123.121.234.12-14'));
        $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.12-14'));
        $this->assertFalse(address_in_subnet('123.121.234.20', '123.121.234.12-14'));
        $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.234.12-14'));
        $this->assertFalse(address_in_subnet('123.12.234.12', '123.121.234.12-14'));
        $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba-babe'));
        $this->assertTrue(address_in_subnet('baba:baba::babc', 'baba:baba::baba-babe'));
        $this->assertTrue(address_in_subnet('baba:baba::babe', 'baba:baba::baba-babe'));
        $this->assertFalse(address_in_subnet('bab:baba::bab0', 'bab:baba::baba-babe'));
        $this->assertFalse(address_in_subnet('bab:baba::babf', 'bab:baba::baba-babe'));
        $this->assertFalse(address_in_subnet('bab:baba::bfbe', 'bab:baba::baba-babe'));
        $this->assertFalse(address_in_subnet('bfb:baba::babe', 'bab:baba::baba-babe'));

129
        // Fixed input.
130
131
132
        $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12 - 14 '));
        $this->assertTrue(address_in_subnet('bab:baba::babe', 'bab:baba::baba - babe  '));

133
        // Incorrect input.
134
135
136
137
        $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-234.14'));
        $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-256'));
        $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12--256'));

138
        // 3: xxx.xxx or xxx.xxx. or xxx:xxx:xxxx or xxx:xxx:xxxx. (incomplete address, a bit non-technical ;-).
139
140
141
142
143
144
145
146
147
148
149
150
151
152
        $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12'));
        $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.23.13'));
        $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.'));
        $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234'));
        $this->assertTrue(address_in_subnet('123.121.234.12', '123.121'));
        $this->assertTrue(address_in_subnet('123.121.234.12', '123'));
        $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234.'));
        $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234'));
        $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba::bab'));
        $this->assertFalse(address_in_subnet('baba:baba::ba', 'baba:baba::bc'));
        $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba'));
        $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:'));
        $this->assertFalse(address_in_subnet('bab:baba::bab', 'baba:'));

153
        // Multiple subnets.
154
155
156
157
158
        $this->assertTrue(address_in_subnet('123.121.234.12', '::1/64, 124., 123.121.234.10-30'));
        $this->assertTrue(address_in_subnet('124.121.234.12', '::1/64, 124., 123.121.234.10-30'));
        $this->assertTrue(address_in_subnet('::2',            '::1/64, 124., 123.121.234.10-30'));
        $this->assertFalse(address_in_subnet('12.121.234.12', '::1/64, 124., 123.121.234.10-30'));

159
        // Other incorrect input.
160
161
162
        $this->assertFalse(address_in_subnet('123.123.123.123', ''));
    }

163
164
    public function test_fix_utf8() {
        // Make sure valid data including other types is not changed.
165
166
167
168
169
        $this->assertSame(null, fix_utf8(null));
        $this->assertSame(1, fix_utf8(1));
        $this->assertSame(1.1, fix_utf8(1.1));
        $this->assertSame(true, fix_utf8(true));
        $this->assertSame('', fix_utf8(''));
170
        $this->assertSame('abc', fix_utf8('abc'));
171
172
173
174
175
176
177
        $array = array('do', 're', 'mi');
        $this->assertSame($array, fix_utf8($array));
        $object = new stdClass();
        $object->a = 'aa';
        $object->b = 'bb';
        $this->assertEquals($object, fix_utf8($object));

Petr Škoda's avatar
Petr Škoda committed
178
179
        // valid utf8 string
        $this->assertSame("žlutý koníček přeskočil potůček \n\t\r", fix_utf8("žlutý koníček přeskočil potůček \n\t\r\0"));
180

181
        // Invalid utf8 string.
182
        $this->assertSame('aš', fix_utf8('a'.chr(130).'š'), 'This fails with buggy iconv() when mbstring extenstion is not available as fallback.');
183
184
    }

185
    public function test_optional_param() {
186
187
188
189
        global $CFG;

        $_POST['username'] = 'post_user';
        $_GET['username'] = 'get_user';
190
        $this->assertSame($_POST['username'], optional_param('username', 'default_user', PARAM_RAW));
191
192

        unset($_POST['username']);
193
        $this->assertSame($_GET['username'], optional_param('username', 'default_user', PARAM_RAW));
194
195

        unset($_GET['username']);
196
        $this->assertSame('default_user', optional_param('username', 'default_user', PARAM_RAW));
197

198
        // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
199
200
201
202
        $_POST['username'] = 'post_user';
        try {
            optional_param('username', 'default_user', null);
            $this->fail('coding_exception expected');
203
204
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
205
206
207
208
        }
        try {
            @optional_param('username', 'default_user');
            $this->fail('coding_exception expected');
209
210
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
211
212
213
        } catch (Error $error) {
            // PHP 7.1 throws Error even earlier.
            $this->assertRegExp('/Too few arguments to function/', $error->getMessage());
214
215
216
217
        }
        try {
            @optional_param('username');
            $this->fail('coding_exception expected');
218
219
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
220
221
222
        } catch (Error $error) {
            // PHP 7.1 throws Error even earlier.
            $this->assertRegExp('/Too few arguments to function/', $error->getMessage());
223
224
225
226
        }
        try {
            optional_param('', 'default_user', PARAM_RAW);
            $this->fail('coding_exception expected');
227
228
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
229
230
        }

231
        // Make sure warning is displayed if array submitted - TODO: throw exception in Moodle 2.3.
232
        $_POST['username'] = array('a'=>'a');
233
        $this->assertSame($_POST['username'], optional_param('username', 'default_user', PARAM_RAW));
234
        $this->assertDebuggingCalled();
235
236
    }

237
    public function test_optional_param_array() {
238
239
240
241
        global $CFG;

        $_POST['username'] = array('a'=>'post_user');
        $_GET['username'] = array('a'=>'get_user');
242
        $this->assertSame($_POST['username'], optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
243
244

        unset($_POST['username']);
245
        $this->assertSame($_GET['username'], optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
246
247

        unset($_GET['username']);
248
        $this->assertSame(array('a'=>'default_user'), optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
249

250
        // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
251
252
253
254
        $_POST['username'] = array('a'=>'post_user');
        try {
            optional_param_array('username', array('a'=>'default_user'), null);
            $this->fail('coding_exception expected');
255
256
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
257
258
259
260
        }
        try {
            @optional_param_array('username', array('a'=>'default_user'));
            $this->fail('coding_exception expected');
261
262
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
263
264
265
        } catch (Error $error) {
            // PHP 7.1 throws Error even earlier.
            $this->assertRegExp('/Too few arguments to function/', $error->getMessage());
266
267
268
269
        }
        try {
            @optional_param_array('username');
            $this->fail('coding_exception expected');
270
271
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
272
273
274
        } catch (Error $error) {
            // PHP 7.1 throws Error even earlier.
            $this->assertRegExp('/Too few arguments to function/', $error->getMessage());
275
276
277
278
        }
        try {
            optional_param_array('', array('a'=>'default_user'), PARAM_RAW);
            $this->fail('coding_exception expected');
279
280
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
281
282
        }

283
        // Do not allow nested arrays.
284
285
286
287
288
289
290
291
        try {
            $_POST['username'] = array('a'=>array('b'=>'post_user'));
            optional_param_array('username', array('a'=>'default_user'), PARAM_RAW);
            $this->fail('coding_exception expected');
        } catch (coding_exception $ex) {
            $this->assertTrue(true);
        }

292
        // Do not allow non-arrays.
293
        $_POST['username'] = 'post_user';
294
        $this->assertSame(array('a'=>'default_user'), optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
295
        $this->assertDebuggingCalled();
296

297
        // Make sure array keys are sanitised.
298
        $_POST['username'] = array('abc123_;-/*-+ '=>'arrggh', 'a1_-'=>'post_user');
299
        $this->assertSame(array('a1_-'=>'post_user'), optional_param_array('username', array(), PARAM_RAW));
300
        $this->assertDebuggingCalled();
301
302
    }

303
    public function test_required_param() {
304
305
        $_POST['username'] = 'post_user';
        $_GET['username'] = 'get_user';
306
        $this->assertSame('post_user', required_param('username', PARAM_RAW));
307
308

        unset($_POST['username']);
309
        $this->assertSame('get_user', required_param('username', PARAM_RAW));
310
311
312

        unset($_GET['username']);
        try {
313
            $this->assertSame('default_user', required_param('username', PARAM_RAW));
314
315
            $this->fail('moodle_exception expected');
        } catch (moodle_exception $ex) {
316
            $this->assertInstanceOf('moodle_exception', $ex);
317
318
        }

319
        // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
320
321
322
323
        $_POST['username'] = 'post_user';
        try {
            @required_param('username');
            $this->fail('coding_exception expected');
324
325
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
326
327
328
        } catch (Error $error) {
            // PHP 7.1 throws Error even earlier.
            $this->assertRegExp('/Too few arguments to function/', $error->getMessage());
329
330
331
332
        }
        try {
            required_param('username', '');
            $this->fail('coding_exception expected');
333
334
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
335
336
337
338
        }
        try {
            required_param('', PARAM_RAW);
            $this->fail('coding_exception expected');
339
340
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
341
342
        }

343
        // Make sure warning is displayed if array submitted - TODO: throw exception in Moodle 2.3.
344
        $_POST['username'] = array('a'=>'a');
345
        $this->assertSame($_POST['username'], required_param('username', PARAM_RAW));
346
        $this->assertDebuggingCalled();
347
348
    }

349
    public function test_required_param_array() {
350
351
352
353
        global $CFG;

        $_POST['username'] = array('a'=>'post_user');
        $_GET['username'] = array('a'=>'get_user');
354
        $this->assertSame($_POST['username'], required_param_array('username', PARAM_RAW));
355
356

        unset($_POST['username']);
357
        $this->assertSame($_GET['username'], required_param_array('username', PARAM_RAW));
358

359
        // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
360
361
362
363
        $_POST['username'] = array('a'=>'post_user');
        try {
            required_param_array('username', null);
            $this->fail('coding_exception expected');
364
365
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
366
367
368
369
        }
        try {
            @required_param_array('username');
            $this->fail('coding_exception expected');
370
371
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
372
373
374
        } catch (Error $error) {
            // PHP 7.1 throws Error.
            $this->assertRegExp('/Too few arguments to function/', $error->getMessage());
375
376
377
378
        }
        try {
            required_param_array('', PARAM_RAW);
            $this->fail('coding_exception expected');
379
380
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
381
382
        }

383
        // Do not allow nested arrays.
384
385
386
387
        try {
            $_POST['username'] = array('a'=>array('b'=>'post_user'));
            required_param_array('username', PARAM_RAW);
            $this->fail('coding_exception expected');
388
389
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
390
391
        }

392
        // Do not allow non-arrays.
393
394
395
396
397
        try {
            $_POST['username'] = 'post_user';
            required_param_array('username', PARAM_RAW);
            $this->fail('moodle_exception expected');
        } catch (moodle_exception $ex) {
398
            $this->assertInstanceOf('moodle_exception', $ex);
399
400
        }

401
        // Make sure array keys are sanitised.
402
        $_POST['username'] = array('abc123_;-/*-+ '=>'arrggh', 'a1_-'=>'post_user');
403
        $this->assertSame(array('a1_-'=>'post_user'), required_param_array('username', PARAM_RAW));
404
        $this->assertDebuggingCalled();
405
406
    }

407
408
    public function test_clean_param() {
        // Forbid objects and arrays.
409
410
411
        try {
            clean_param(array('x', 'y'), PARAM_RAW);
            $this->fail('coding_exception expected');
412
413
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
414
415
416
417
418
419
        }
        try {
            $param = new stdClass();
            $param->id = 1;
            clean_param($param, PARAM_RAW);
            $this->fail('coding_exception expected');
420
421
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
422
423
        }

424
        // Require correct type.
425
426
427
428
        try {
            clean_param('x', 'xxxxxx');
            $this->fail('moodle_exception expected');
        } catch (moodle_exception $ex) {
429
            $this->assertInstanceOf('moodle_exception', $ex);
430
431
432
433
434
        }
        try {
            @clean_param('x');
            $this->fail('moodle_exception expected');
        } catch (moodle_exception $ex) {
435
            $this->assertInstanceOf('moodle_exception', $ex);
436
437
438
        } catch (Error $error) {
            // PHP 7.1 throws Error even earlier.
            $this->assertRegExp('/Too few arguments to function/', $error->getMessage());
439
440
441
        }
    }

442
443
444
445
    public function test_clean_param_array() {
        $this->assertSame(array(), clean_param_array(null, PARAM_RAW));
        $this->assertSame(array('a', 'b'), clean_param_array(array('a', 'b'), PARAM_RAW));
        $this->assertSame(array('a', array('b')), clean_param_array(array('a', array('b')), PARAM_RAW, true));
446

447
        // Require correct type.
448
449
450
451
        try {
            clean_param_array(array('x'), 'xxxxxx');
            $this->fail('moodle_exception expected');
        } catch (moodle_exception $ex) {
452
            $this->assertInstanceOf('moodle_exception', $ex);
453
454
455
456
457
        }
        try {
            @clean_param_array(array('x'));
            $this->fail('moodle_exception expected');
        } catch (moodle_exception $ex) {
458
            $this->assertInstanceOf('moodle_exception', $ex);
459
460
461
        } catch (Error $error) {
            // PHP 7.1 throws Error even earlier.
            $this->assertRegExp('/Too few arguments to function/', $error->getMessage());
462
463
464
465
466
        }

        try {
            clean_param_array(array('x', array('y')), PARAM_RAW);
            $this->fail('coding_exception expected');
467
468
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('coding_exception', $ex);
469
470
        }

471
        // Test recursive.
472
473
    }

474
475
476
477
    public function test_clean_param_raw() {
        $this->assertSame(
            '#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)',
            clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_RAW));
478
479
    }

480
481
    public function test_clean_param_trim() {
        $this->assertSame('Frog toad', clean_param("   Frog toad   \r\n  ", PARAM_RAW_TRIMMED));
482
483
    }

484
485
486
487
    public function test_clean_param_clean() {
        // PARAM_CLEAN is an ugly hack, do not use in new code (skodak),
        // instead use more specific type, or submit sothing that can be verified properly.
        $this->assertSame('xx', clean_param('xx<script>', PARAM_CLEAN));
488
489
    }

490
491
    public function test_clean_param_alpha() {
        $this->assertSame('DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHA));
492
493
    }

494
495
    public function test_clean_param_alphanum() {
        $this->assertSame('978942897DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHANUM));
496
497
    }

498
499
    public function test_clean_param_alphaext() {
        $this->assertSame('DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHAEXT));
500
501
    }

502
503
    public function test_clean_param_sequence() {
        $this->assertSame(',9789,42897', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_SEQUENCE));
504
505
    }

506
507
508
509
510
511
512
513
514
    public function test_clean_param_component() {
        // Please note the cleaning of component names is very strict, no guessing here.
        $this->assertSame('mod_forum', clean_param('mod_forum', PARAM_COMPONENT));
        $this->assertSame('block_online_users', clean_param('block_online_users', PARAM_COMPONENT));
        $this->assertSame('block_blond_online_users', clean_param('block_blond_online_users', PARAM_COMPONENT));
        $this->assertSame('mod_something2', clean_param('mod_something2', PARAM_COMPONENT));
        $this->assertSame('forum', clean_param('forum', PARAM_COMPONENT));
        $this->assertSame('user', clean_param('user', PARAM_COMPONENT));
        $this->assertSame('rating', clean_param('rating', PARAM_COMPONENT));
515
516
        $this->assertSame('feedback360', clean_param('feedback360', PARAM_COMPONENT));
        $this->assertSame('mod_feedback360', clean_param('mod_feedback360', PARAM_COMPONENT));
517
518
519
520
521
522
        $this->assertSame('', clean_param('mod_2something', PARAM_COMPONENT));
        $this->assertSame('', clean_param('2mod_something', PARAM_COMPONENT));
        $this->assertSame('', clean_param('mod_something_xx', PARAM_COMPONENT));
        $this->assertSame('', clean_param('auth_something__xx', PARAM_COMPONENT));
        $this->assertSame('', clean_param('mod_Something', PARAM_COMPONENT));
        $this->assertSame('', clean_param('mod_somethíng', PARAM_COMPONENT));
523
        $this->assertSame('', clean_param('mod__something', PARAM_COMPONENT));
524
525
        $this->assertSame('', clean_param('auth_xx-yy', PARAM_COMPONENT));
        $this->assertSame('', clean_param('_auth_xx', PARAM_COMPONENT));
526
        $this->assertSame('a2uth_xx', clean_param('a2uth_xx', PARAM_COMPONENT));
527
528
529
530
531
        $this->assertSame('', clean_param('auth_xx_', PARAM_COMPONENT));
        $this->assertSame('', clean_param('auth_xx.old', PARAM_COMPONENT));
        $this->assertSame('', clean_param('_user', PARAM_COMPONENT));
        $this->assertSame('', clean_param('2rating', PARAM_COMPONENT));
        $this->assertSame('', clean_param('user_', PARAM_COMPONENT));
532
533
    }

534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
    public function test_clean_param_localisedfloat() {

        $this->assertSame(0.5, clean_param('0.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('0X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(0.5, clean_param('.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(10.5, clean_param('10.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('10X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(1000.5, clean_param('1 000.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('1 000X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('1.000.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('1X000X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('nan', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('10.6blah', PARAM_LOCALISEDFLOAT));

        // Tests with a localised decimal separator.
        $this->define_local_decimal_separator();

        $this->assertSame(0.5, clean_param('0.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(0.5, clean_param('0X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(0.5, clean_param('.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(0.5, clean_param('X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(10.5, clean_param('10.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(10.5, clean_param('10X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(1000.5, clean_param('1 000.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(1000.5, clean_param('1 000X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('1.000.5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('1X000X5', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('nan', PARAM_LOCALISEDFLOAT));
        $this->assertSame(false, clean_param('10X6blah', PARAM_LOCALISEDFLOAT));
    }

566
    public function test_is_valid_plugin_name() {
567
568
        $this->assertTrue(is_valid_plugin_name('forum'));
        $this->assertTrue(is_valid_plugin_name('forum2'));
569
        $this->assertTrue(is_valid_plugin_name('feedback360'));
570
571
572
573
574
575
576
577
578
579
580
581
        $this->assertTrue(is_valid_plugin_name('online_users'));
        $this->assertTrue(is_valid_plugin_name('blond_online_users'));
        $this->assertFalse(is_valid_plugin_name('online__users'));
        $this->assertFalse(is_valid_plugin_name('forum '));
        $this->assertFalse(is_valid_plugin_name('forum.old'));
        $this->assertFalse(is_valid_plugin_name('xx-yy'));
        $this->assertFalse(is_valid_plugin_name('2xx'));
        $this->assertFalse(is_valid_plugin_name('Xx'));
        $this->assertFalse(is_valid_plugin_name('_xx'));
        $this->assertFalse(is_valid_plugin_name('xx_'));
    }

582
583
584
585
    public function test_clean_param_plugin() {
        // Please note the cleaning of plugin names is very strict, no guessing here.
        $this->assertSame('forum', clean_param('forum', PARAM_PLUGIN));
        $this->assertSame('forum2', clean_param('forum2', PARAM_PLUGIN));
586
        $this->assertSame('feedback360', clean_param('feedback360', PARAM_PLUGIN));
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
        $this->assertSame('online_users', clean_param('online_users', PARAM_PLUGIN));
        $this->assertSame('blond_online_users', clean_param('blond_online_users', PARAM_PLUGIN));
        $this->assertSame('', clean_param('online__users', PARAM_PLUGIN));
        $this->assertSame('', clean_param('forum ', PARAM_PLUGIN));
        $this->assertSame('', clean_param('forum.old', PARAM_PLUGIN));
        $this->assertSame('', clean_param('xx-yy', PARAM_PLUGIN));
        $this->assertSame('', clean_param('2xx', PARAM_PLUGIN));
        $this->assertSame('', clean_param('Xx', PARAM_PLUGIN));
        $this->assertSame('', clean_param('_xx', PARAM_PLUGIN));
        $this->assertSame('', clean_param('xx_', PARAM_PLUGIN));
    }

    public function test_clean_param_area() {
        // Please note the cleaning of area names is very strict, no guessing here.
        $this->assertSame('something', clean_param('something', PARAM_AREA));
        $this->assertSame('something2', clean_param('something2', PARAM_AREA));
        $this->assertSame('some_thing', clean_param('some_thing', PARAM_AREA));
        $this->assertSame('some_thing_xx', clean_param('some_thing_xx', PARAM_AREA));
605
        $this->assertSame('feedback360', clean_param('feedback360', PARAM_AREA));
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
        $this->assertSame('', clean_param('_something', PARAM_AREA));
        $this->assertSame('', clean_param('something_', PARAM_AREA));
        $this->assertSame('', clean_param('2something', PARAM_AREA));
        $this->assertSame('', clean_param('Something', PARAM_AREA));
        $this->assertSame('', clean_param('some-thing', PARAM_AREA));
        $this->assertSame('', clean_param('somethííng', PARAM_AREA));
        $this->assertSame('', clean_param('something.x', PARAM_AREA));
    }

    public function test_clean_param_text() {
        $this->assertSame(PARAM_TEXT, PARAM_MULTILANG);
        // Standard.
        $this->assertSame('xx<lang lang="en">aa</lang><lang lang="yy">pp</lang>', clean_param('xx<lang lang="en">aa</lang><lang lang="yy">pp</lang>', PARAM_TEXT));
        $this->assertSame('<span lang="en" class="multilang">aa</span><span lang="xy" class="multilang">bb</span>', clean_param('<span lang="en" class="multilang">aa</span><span lang="xy" class="multilang">bb</span>', PARAM_TEXT));
        $this->assertSame('xx<lang lang="en">aa'."\n".'</lang><lang lang="yy">pp</lang>', clean_param('xx<lang lang="en">aa'."\n".'</lang><lang lang="yy">pp</lang>', PARAM_TEXT));
        // Malformed.
        $this->assertSame('<span lang="en" class="multilang">aa</span>', clean_param('<span lang="en" class="multilang">aa</span>', PARAM_TEXT));
        $this->assertSame('aa', clean_param('<span lang="en" class="nothing" class="multilang">aa</span>', PARAM_TEXT));
        $this->assertSame('aa', clean_param('<lang lang="en" class="multilang">aa</lang>', PARAM_TEXT));
        $this->assertSame('aa', clean_param('<lang lang="en!!">aa</lang>', PARAM_TEXT));
        $this->assertSame('aa', clean_param('<span lang="en==" class="multilang">aa</span>', PARAM_TEXT));
        $this->assertSame('abc', clean_param('a<em>b</em>c', PARAM_TEXT));
        $this->assertSame('a>c>', clean_param('a><xx >c>', PARAM_TEXT)); // Standard strip_tags() behaviour.
        $this->assertSame('a', clean_param('a<b', PARAM_TEXT));
        $this->assertSame('a>b', clean_param('a>b', PARAM_TEXT));
        $this->assertSame('<lang lang="en">a>a</lang>', clean_param('<lang lang="en">a>a</lang>', PARAM_TEXT)); // Standard strip_tags() behaviour.
        $this->assertSame('a', clean_param('<lang lang="en">a<a</lang>', PARAM_TEXT));
        $this->assertSame('<lang lang="en">aa</lang>', clean_param('<lang lang="en">a<br>a</lang>', PARAM_TEXT));
    }

    public function test_clean_param_url() {
        // Test PARAM_URL and PARAM_LOCALURL a bit.
638
        // Valid URLs.
639
640
641
642
        $this->assertSame('http://google.com/', clean_param('http://google.com/', PARAM_URL));
        $this->assertSame('http://some.very.long.and.silly.domain/with/a/path/', clean_param('http://some.very.long.and.silly.domain/with/a/path/', PARAM_URL));
        $this->assertSame('http://localhost/', clean_param('http://localhost/', PARAM_URL));
        $this->assertSame('http://0.255.1.1/numericip.php', clean_param('http://0.255.1.1/numericip.php', PARAM_URL));
643
644
645
646
647
        $this->assertSame('https://google.com/', clean_param('https://google.com/', PARAM_URL));
        $this->assertSame('https://some.very.long.and.silly.domain/with/a/path/', clean_param('https://some.very.long.and.silly.domain/with/a/path/', PARAM_URL));
        $this->assertSame('https://localhost/', clean_param('https://localhost/', PARAM_URL));
        $this->assertSame('https://0.255.1.1/numericip.php', clean_param('https://0.255.1.1/numericip.php', PARAM_URL));
        $this->assertSame('ftp://ftp.debian.org/debian/', clean_param('ftp://ftp.debian.org/debian/', PARAM_URL));
648
        $this->assertSame('/just/a/path', clean_param('/just/a/path', PARAM_URL));
649
        // Invalid URLs.
650
        $this->assertSame('', clean_param('funny:thing', PARAM_URL));
651
652
653
654
655
        $this->assertSame('', clean_param('http://example.ee/sdsf"f', PARAM_URL));
        $this->assertSame('', clean_param('javascript://comment%0Aalert(1)', PARAM_URL));
        $this->assertSame('', clean_param('rtmp://example.com/livestream', PARAM_URL));
        $this->assertSame('', clean_param('rtmp://example.com/live&foo', PARAM_URL));
        $this->assertSame('', clean_param('rtmp://example.com/fms&mp4:path/to/file.mp4', PARAM_URL));
656
657
658
        $this->assertSame('', clean_param('mailto:support@moodle.org', PARAM_URL));
        $this->assertSame('', clean_param('mailto:support@moodle.org?subject=Hello%20Moodle', PARAM_URL));
        $this->assertSame('', clean_param('mailto:support@moodle.org?subject=Hello%20Moodle&cc=feedback@moodle.org', PARAM_URL));
659
660
661
    }

    public function test_clean_param_localurl() {
662
        global $CFG;
663
664
665

        $this->resetAfterTest();

666
667
        // External, invalid.
        $this->assertSame('', clean_param('funny:thing', PARAM_LOCALURL));
668
        $this->assertSame('', clean_param('http://google.com/', PARAM_LOCALURL));
669
        $this->assertSame('', clean_param('https://google.com/?test=true', PARAM_LOCALURL));
670
        $this->assertSame('', clean_param('http://some.very.long.and.silly.domain/with/a/path/', PARAM_LOCALURL));
671
672

        // Local absolute.
673
        $this->assertSame(clean_param($CFG->wwwroot, PARAM_LOCALURL), $CFG->wwwroot);
674
675
676
677
        $this->assertSame($CFG->wwwroot . '/with/something?else=true',
            clean_param($CFG->wwwroot . '/with/something?else=true', PARAM_LOCALURL));

        // Local relative.
678
679
        $this->assertSame('/just/a/path', clean_param('/just/a/path', PARAM_LOCALURL));
        $this->assertSame('course/view.php?id=3', clean_param('course/view.php?id=3', PARAM_LOCALURL));
680

681
682
        // Local absolute HTTPS in a non HTTPS site.
        $CFG->wwwroot = str_replace('https:', 'http:', $CFG->wwwroot); // Need to simulate non-https site.
683
684
685
        $httpsroot = str_replace('http:', 'https:', $CFG->wwwroot);
        $this->assertSame('', clean_param($httpsroot, PARAM_LOCALURL));
        $this->assertSame('', clean_param($httpsroot . '/with/something?else=true', PARAM_LOCALURL));
686
687

        // Local absolute HTTPS in a HTTPS site.
688
        $CFG->wwwroot = str_replace('http:', 'https:', $CFG->wwwroot);
689
        $httpsroot = $CFG->wwwroot;
690
691
692
        $this->assertSame($httpsroot, clean_param($httpsroot, PARAM_LOCALURL));
        $this->assertSame($httpsroot . '/with/something?else=true',
            clean_param($httpsroot . '/with/something?else=true', PARAM_LOCALURL));
693
694
695
696

        // Test open redirects are not possible.
        $CFG->wwwroot = 'http://www.example.com';
        $this->assertSame('', clean_param('http://www.example.com.evil.net/hack.php', PARAM_LOCALURL));
697
        $CFG->wwwroot = 'https://www.example.com';
698
        $this->assertSame('', clean_param('https://www.example.com.evil.net/hack.php', PARAM_LOCALURL));
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
    }

    public function test_clean_param_file() {
        $this->assertSame('correctfile.txt', clean_param('correctfile.txt', PARAM_FILE));
        $this->assertSame('badfile.txt', clean_param('b\'a<d`\\/fi:l>e.t"x|t', PARAM_FILE));
        $this->assertSame('..parentdirfile.txt', clean_param('../parentdirfile.txt', PARAM_FILE));
        $this->assertSame('....grandparentdirfile.txt', clean_param('../../grandparentdirfile.txt', PARAM_FILE));
        $this->assertSame('..winparentdirfile.txt', clean_param('..\winparentdirfile.txt', PARAM_FILE));
        $this->assertSame('....wingrandparentdir.txt', clean_param('..\..\wingrandparentdir.txt', PARAM_FILE));
        $this->assertSame('myfile.a.b.txt', clean_param('myfile.a.b.txt', PARAM_FILE));
        $this->assertSame('myfile..a..b.txt', clean_param('myfile..a..b.txt', PARAM_FILE));
        $this->assertSame('myfile.a..b...txt', clean_param('myfile.a..b...txt', PARAM_FILE));
        $this->assertSame('myfile.a.txt', clean_param('myfile.a.txt', PARAM_FILE));
        $this->assertSame('myfile...txt', clean_param('myfile...txt', PARAM_FILE));
        $this->assertSame('...jpg', clean_param('...jpg', PARAM_FILE));
        $this->assertSame('.a.b.', clean_param('.a.b.', PARAM_FILE));
        $this->assertSame('', clean_param('.', PARAM_FILE));
        $this->assertSame('', clean_param('..', PARAM_FILE));
        $this->assertSame('...', clean_param('...', PARAM_FILE));
        $this->assertSame('. . . .', clean_param('. . . .', PARAM_FILE));
        $this->assertSame('dontrtrim.me. .. .. . ', clean_param('dontrtrim.me. .. .. . ', PARAM_FILE));
        $this->assertSame(' . .dontltrim.me', clean_param(' . .dontltrim.me', PARAM_FILE));
        $this->assertSame('here is a tab.txt', clean_param("here is a tab\t.txt", PARAM_FILE));
        $this->assertSame('here is a linebreak.txt', clean_param("here is a line\r\nbreak.txt", PARAM_FILE));

        // The following behaviours have been maintained although they seem a little odd.
        $this->assertSame('funnything', clean_param('funny:thing', PARAM_FILE));
        $this->assertSame('.currentdirfile.txt', clean_param('./currentdirfile.txt', PARAM_FILE));
        $this->assertSame('ctempwindowsfile.txt', clean_param('c:\temp\windowsfile.txt', PARAM_FILE));
        $this->assertSame('homeuserlinuxfile.txt', clean_param('/home/user/linuxfile.txt', PARAM_FILE));
        $this->assertSame('~myfile.txt', clean_param('~/myfile.txt', PARAM_FILE));
    }

    public function test_clean_param_path() {
        $this->assertSame('correctfile.txt', clean_param('correctfile.txt', PARAM_PATH));
        $this->assertSame('bad/file.txt', clean_param('b\'a<d`\\/fi:l>e.t"x|t', PARAM_PATH));
        $this->assertSame('/parentdirfile.txt', clean_param('../parentdirfile.txt', PARAM_PATH));
        $this->assertSame('/grandparentdirfile.txt', clean_param('../../grandparentdirfile.txt', PARAM_PATH));
        $this->assertSame('/winparentdirfile.txt', clean_param('..\winparentdirfile.txt', PARAM_PATH));
        $this->assertSame('/wingrandparentdir.txt', clean_param('..\..\wingrandparentdir.txt', PARAM_PATH));
        $this->assertSame('funnything', clean_param('funny:thing', PARAM_PATH));
        $this->assertSame('./here', clean_param('./././here', PARAM_PATH));
        $this->assertSame('./currentdirfile.txt', clean_param('./currentdirfile.txt', PARAM_PATH));
        $this->assertSame('c/temp/windowsfile.txt', clean_param('c:\temp\windowsfile.txt', PARAM_PATH));
        $this->assertSame('/home/user/linuxfile.txt', clean_param('/home/user/linuxfile.txt', PARAM_PATH));
        $this->assertSame('/home../user ./.linuxfile.txt', clean_param('/home../user ./.linuxfile.txt', PARAM_PATH));
        $this->assertSame('~/myfile.txt', clean_param('~/myfile.txt', PARAM_PATH));
        $this->assertSame('~/myfile.txt', clean_param('~/../myfile.txt', PARAM_PATH));
        $this->assertSame('/..b../.../myfile.txt', clean_param('/..b../.../myfile.txt', PARAM_PATH));
        $this->assertSame('..b../.../myfile.txt', clean_param('..b../.../myfile.txt', PARAM_PATH));
        $this->assertSame('/super/slashes/', clean_param('/super//slashes///', PARAM_PATH));
    }

    public function test_clean_param_username() {
753
754
755
        global $CFG;
        $currentstatus =  $CFG->extendedusernamechars;

756
757
758
759
760
761
762
763
764
765
        // Run tests with extended character == false;.
        $CFG->extendedusernamechars = false;
        $this->assertSame('johndoe123', clean_param('johndoe123', PARAM_USERNAME) );
        $this->assertSame('john.doe', clean_param('john.doe', PARAM_USERNAME));
        $this->assertSame('john-doe', clean_param('john-doe', PARAM_USERNAME));
        $this->assertSame('john-doe', clean_param('john- doe', PARAM_USERNAME));
        $this->assertSame('john_doe', clean_param('john_doe', PARAM_USERNAME));
        $this->assertSame('john@doe', clean_param('john@doe', PARAM_USERNAME));
        $this->assertSame('johndoe', clean_param('john~doe', PARAM_USERNAME));
        $this->assertSame('johndoe', clean_param('john´doe', PARAM_USERNAME));
766
767
        $this->assertSame(clean_param('john# $%&()+_^', PARAM_USERNAME), 'john_');
        $this->assertSame(clean_param(' john# $%&()+_^ ', PARAM_USERNAME), 'john_');
768
769
770
771
772
773
774
775
        $this->assertSame(clean_param('john#$%&() ', PARAM_USERNAME), 'john');
        $this->assertSame('johnd', clean_param('JOHNdóé ', PARAM_USERNAME));
        $this->assertSame(clean_param('john.,:;-_/|\ñÑ[]A_X-,D {} ~!@#$%^&*()_+ ?><[] ščřžžý ?ýáž?žý??šdoe ', PARAM_USERNAME), 'john.-_a_x-d@_doe');

        // Test success condition, if extendedusernamechars == ENABLE;.
        $CFG->extendedusernamechars = true;
        $this->assertSame('john_doe', clean_param('john_doe', PARAM_USERNAME));
        $this->assertSame('john@doe', clean_param('john@doe', PARAM_USERNAME));
776
777
        $this->assertSame(clean_param('john# $%&()+_^', PARAM_USERNAME), 'john# $%&()+_^');
        $this->assertSame(clean_param(' john# $%&()+_^ ', PARAM_USERNAME), 'john# $%&()+_^');
778
779
780
781
        $this->assertSame('john~doe', clean_param('john~doe', PARAM_USERNAME));
        $this->assertSame('john´doe', clean_param('joHN´doe', PARAM_USERNAME));
        $this->assertSame('johndoe', clean_param('johnDOE', PARAM_USERNAME));
        $this->assertSame('johndóé', clean_param('johndóé ', PARAM_USERNAME));
782
783
784
785

        $CFG->extendedusernamechars = $currentstatus;
    }

786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
    public function test_clean_param_stringid() {
        // Test string identifiers validation.
        // Valid strings.
        $this->assertSame('validstring', clean_param('validstring', PARAM_STRINGID));
        $this->assertSame('mod/foobar:valid_capability', clean_param('mod/foobar:valid_capability', PARAM_STRINGID));
        $this->assertSame('CZ', clean_param('CZ', PARAM_STRINGID));
        $this->assertSame('application/vnd.ms-powerpoint', clean_param('application/vnd.ms-powerpoint', PARAM_STRINGID));
        $this->assertSame('grade2', clean_param('grade2', PARAM_STRINGID));
        // Invalid strings.
        $this->assertSame('', clean_param('trailing ', PARAM_STRINGID));
        $this->assertSame('', clean_param('space bar', PARAM_STRINGID));
        $this->assertSame('', clean_param('0numeric', PARAM_STRINGID));
        $this->assertSame('', clean_param('*', PARAM_STRINGID));
        $this->assertSame('', clean_param(' ', PARAM_STRINGID));
    }

    public function test_clean_param_timezone() {
        // Test timezone validation.
804
805
806
807
808
809
810
811
812
813
814
815
        $testvalues = array (
            'America/Jamaica'                => 'America/Jamaica',
            'America/Argentina/Cordoba'      => 'America/Argentina/Cordoba',
            'America/Port-au-Prince'         => 'America/Port-au-Prince',
            'America/Argentina/Buenos_Aires' => 'America/Argentina/Buenos_Aires',
            'PST8PDT'                        => 'PST8PDT',
            'Wrong.Value'                    => '',
            'Wrong/.Value'                   => '',
            'Wrong(Value)'                   => '',
            '0'                              => '0',
            '0.0'                            => '0.0',
            '0.5'                            => '0.5',
816
817
818
819
820
821
822
823
824
825
            '9.0'                            => '9.0',
            '-9.0'                           => '-9.0',
            '+9.0'                           => '+9.0',
            '9.5'                            => '9.5',
            '-9.5'                           => '-9.5',
            '+9.5'                           => '+9.5',
            '12.0'                           => '12.0',
            '-12.0'                          => '-12.0',
            '+12.0'                          => '+12.0',
            '12.5'                           => '12.5',
826
827
            '-12.5'                          => '-12.5',
            '+12.5'                          => '+12.5',
828
829
830
            '13.0'                           => '13.0',
            '-13.0'                          => '-13.0',
            '+13.0'                          => '+13.0',
831
            '13.5'                           => '',
832
            '+13.5'                          => '',
833
834
835
836
837
            '-13.5'                          => '',
            '0.2'                            => '');

        foreach ($testvalues as $testvalue => $expectedvalue) {
            $actualvalue = clean_param($testvalue, PARAM_TIMEZONE);
838
            $this->assertEquals($expectedvalue, $actualvalue);
839
840
841
        }
    }

842
    public function test_validate_param() {
843
844
845
        try {
            $param = validate_param('11a', PARAM_INT);
            $this->fail('invalid_parameter_exception expected');
846
847
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('invalid_parameter_exception', $ex);
848
        }
849
850
851
852

        $param = validate_param('11', PARAM_INT);
        $this->assertSame(11, $param);

853
854
855
        try {
            $param = validate_param(null, PARAM_INT, false);
            $this->fail('invalid_parameter_exception expected');
856
857
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('invalid_parameter_exception', $ex);
858
        }
859
860
861
862

        $param = validate_param(null, PARAM_INT, true);
        $this->assertSame(null, $param);

863
864
865
        try {
            $param = validate_param(array(), PARAM_INT);
            $this->fail('invalid_parameter_exception expected');
866
867
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('invalid_parameter_exception', $ex);
868
869
870
871
        }
        try {
            $param = validate_param(new stdClass, PARAM_INT);
            $this->fail('invalid_parameter_exception expected');
872
873
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('invalid_parameter_exception', $ex);
874
        }
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891

        $param = validate_param('1.0', PARAM_FLOAT);
        $this->assertSame(1.0, $param);

        // Make sure valid floats do not cause exception.
        validate_param(1.0, PARAM_FLOAT);
        validate_param(10, PARAM_FLOAT);
        validate_param('0', PARAM_FLOAT);
        validate_param('119813454.545464564564546564545646556564465465456465465465645645465645645645', PARAM_FLOAT);
        validate_param('011.1', PARAM_FLOAT);
        validate_param('11', PARAM_FLOAT);
        validate_param('+.1', PARAM_FLOAT);
        validate_param('-.1', PARAM_FLOAT);
        validate_param('1e10', PARAM_FLOAT);
        validate_param('.1e+10', PARAM_FLOAT);
        validate_param('1E-1', PARAM_FLOAT);

892
893
894
        try {
            $param = validate_param('1,2', PARAM_FLOAT);
            $this->fail('invalid_parameter_exception expected');
895
896
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('invalid_parameter_exception', $ex);
897
898
899
900
        }
        try {
            $param = validate_param('', PARAM_FLOAT);
            $this->fail('invalid_parameter_exception expected');
901
902
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('invalid_parameter_exception', $ex);
903
904
905
906
        }
        try {
            $param = validate_param('.', PARAM_FLOAT);
            $this->fail('invalid_parameter_exception expected');
907
908
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('invalid_parameter_exception', $ex);
909
910
911
912
        }
        try {
            $param = validate_param('e10', PARAM_FLOAT);
            $this->fail('invalid_parameter_exception expected');
913
914
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('invalid_parameter_exception', $ex);
915
916
917
918
        }
        try {
            $param = validate_param('abc', PARAM_FLOAT);
            $this->fail('invalid_parameter_exception expected');
919
920
        } catch (moodle_exception $ex) {
            $this->assertInstanceOf('invalid_parameter_exception', $ex);
921
        }
922
923
    }

924
    public function test_shorten_text_no_tags_already_short_enough() {
925
        // ......12345678901234567890123456.
926
        $text = "short text already no tags";
927
        $this->assertSame($text, shorten_text($text));
928
    }
929

930
    public function test_shorten_text_with_tags_already_short_enough() {
931
        // .........123456...7890....12345678.......901234567.
932
        $text = "<p>short <b>text</b> already</p><p>with tags</p>";
933
        $this->assertSame($text, shorten_text($text));
934
    }
935

936
    public function test_shorten_text_no_tags_needs_shortening() {
937
938
        // Default truncation is after 30 chars, but allowing 3 for the final '...'.
        // ......12345678901234567890123456789023456789012345678901234.
939
        $text = "long text without any tags blah de blah blah blah what";
940
        $this->assertSame('long text without any tags ...', shorten_text($text));
941
    }
942

943
    public function test_shorten_text_with_tags_needs_shortening() {
944
        // .......................................123456789012345678901234567890...
945
946
947
948
        $text = "<div class='frog'><p><blockquote>Long text with tags that will ".
            "be chopped off but <b>should be added back again</b></blockquote></p></div>";
        $this->assertEquals("<div class='frog'><p><blockquote>Long text with " .
            "tags that ...</blockquote></p></div>", shorten_text($text));
949
    }
950

951
952
953
954
955
956
957
958
    public function test_shorten_text_with_tags_and_html_comment() {
        $text = "<div class='frog'><p><blockquote><!--[if !IE]><!-->Long text with ".
            "tags that will<!--<![endif]--> ".
            "be chopped off but <b>should be added back again</b></blockquote></p></div>";
        $this->assertEquals("<div class='frog'><p><blockquote><!--[if !IE]><!-->Long text with " .
            "tags that ...<!--<![endif]--></blockquote></p></div>", shorten_text($text));
    }

959
    public function test_shorten_text_with_entities() {
960
961
        // Remember to allow 3 chars for the final '...'.
        // ......123456789012345678901234567_____890...
962
        $text = "some text which shouldn't &nbsp; break there";
963
964
965
        $this->assertSame("some text which shouldn't &nbsp; ...", shorten_text($text, 31));
        $this->assertSame("some text which shouldn't &nbsp;...", shorten_text($text, 30));
        $this->assertSame("some text which shouldn't ...", shorten_text($text, 29));
966
    }
967

968
    public function test_shorten_text_known_tricky_case() {
969
        // This case caused a bug up to 1.9.5
970
        // ..........123456789012345678901234567890123456789.....0_____1___2___...
971
        $text = "<h3>standard 'break-out' sub groups in TGs?</h3>&nbsp;&lt;&lt;There are several";
972
        $this->assertSame("<h3>standard 'break-out' sub groups in ...</h3>",
973
            shorten_text($text, 41));
974
        $this->assertSame("<h3>standard 'break-out' sub groups in TGs?...</h3>",
975
            shorten_text($text, 42));
976
        $this->assertSame("<h3>standard 'break-out' sub groups in TGs?</h3>&nbsp;...",
977
            shorten_text($text, 43));
978
    }
979

980
    public function test_shorten_text_no_spaces() {
981
982
        // ..........123456789.
        $text = "<h1>123456789</h1>"; // A string with no convenient breaks.
983
        $this->assertSame("<h1>12345...</h1>", shorten_text($text, 8));
984
    }
985

986
    public function test_shorten_text_utf8_european() {
987
988
        // Text without tags.
        // ......123456789012345678901234567.
989
        $text = "Žluťoučký koníček přeskočil";
990
991
992
        $this->assertSame($text, shorten_text($text)); // 30 chars by default.
        $this->assertSame("Žluťoučký koníče...", shorten_text($text, 19, true));
        $this->assertSame("Žluťoučký ...", shorten_text($text, 19, false));
993
        // And try it with 2-less (that are, in bytes, the middle of a sequence).
994
995
        $this->assertSame("Žluťoučký koní...", shorten_text($text, 17, true));
        $this->assertSame("Žluťoučký ...", shorten_text($text, 17, false));
996

997
        // .........123456789012345678...901234567....89012345.
998
        $text = "<p>Žluťoučký koníček <b>přeskočil</b> potůček</p>";
999
1000
        $this->assertSame($text, shorten_text($text, 60));
        $this->assertSame("<p>Žluťoučký koníček ...</p>", shorten_text($text, 21));
For faster browsing, not all history is shown. View entire blame