Commit | Line | Data |
---|---|---|
a3d5830a PS |
1 | <?php |
2 | // This file is part of Moodle - http://moodle.org/ | |
3 | // | |
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. | |
8 | // | |
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. | |
13 | // | |
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/>. | |
16 | ||
17 | /** | |
18 | * Unit tests for (some of) ../moodlelib.php. | |
19 | * | |
20 | * @package core | |
21 | * @category phpunit | |
22 | * @copyright © 2006 The Open University | |
23 | * @author T.J.Hunt@open.ac.uk | |
24 | * @author nicolas@moodle.com | |
25 | */ | |
26 | ||
27 | defined('MOODLE_INTERNAL') || die(); | |
28 | ||
be6ce11c | 29 | class core_moodlelib_testcase extends advanced_testcase { |
a3d5830a PS |
30 | |
31 | public static $includecoverage = array('lib/moodlelib.php'); | |
32 | ||
d381fb77 GPL |
33 | /** |
34 | * Define a local decimal separator. | |
35 | * | |
36 | * It is not possible to directly change the result of get_string in | |
37 | * a unit test. Instead, we create a language pack for language 'xx' in | |
38 | * dataroot and make langconfig.php with the string we need to change. | |
39 | * The example separator used here is 'X'; on PHP 5.3 and before this | |
40 | * must be a single byte character due to PHP bug/limitation in | |
41 | * number_format, so you can't use UTF-8 characters. | |
d381fb77 GPL |
42 | */ |
43 | protected function define_local_decimal_separator() { | |
44 | global $SESSION, $CFG; | |
45 | ||
46 | $SESSION->lang = 'xx'; | |
47 | $langconfig = "<?php\n\$string['decsep'] = 'X';"; | |
48 | $langfolder = $CFG->dataroot . '/lang/xx'; | |
49 | check_dir_exists($langfolder); | |
50 | file_put_contents($langfolder . '/langconfig.php', $langconfig); | |
51 | } | |
52 | ||
be6ce11c PS |
53 | public function test_cleanremoteaddr() { |
54 | // IPv4. | |
55 | $this->assertNull(cleanremoteaddr('1023.121.234.1')); | |
56 | $this->assertSame('123.121.234.1', cleanremoteaddr('123.121.234.01 ')); | |
a3d5830a | 57 | |
be6ce11c PS |
58 | // IPv6. |
59 | $this->assertNull(cleanremoteaddr('0:0:0:0:0:0:0:0:0')); | |
60 | $this->assertNull(cleanremoteaddr('0:0:0:0:0:0:0:abh')); | |
61 | $this->assertNull(cleanremoteaddr('0:0:0:::0:0:1')); | |
62 | $this->assertSame('::', cleanremoteaddr('0:0:0:0:0:0:0:0', true)); | |
63 | $this->assertSame('::1:1', cleanremoteaddr('0:0:0:0:0:0:1:1', true)); | |
64 | $this->assertSame('abcd:ef::', cleanremoteaddr('abcd:00ef:0:0:0:0:0:0', true)); | |
65 | $this->assertSame('1::1', cleanremoteaddr('1:0:0:0:0:0:0:1', true)); | |
66 | $this->assertSame('0:0:0:0:0:0:10:1', cleanremoteaddr('::10:1', false)); | |
67 | $this->assertSame('1:1:0:0:0:0:0:0', cleanremoteaddr('01:1::', false)); | |
68 | $this->assertSame('10:0:0:0:0:0:0:10', cleanremoteaddr('10::10', false)); | |
69 | $this->assertSame('::ffff:c0a8:11', cleanremoteaddr('::ffff:192.168.1.1', true)); | |
a3d5830a PS |
70 | } |
71 | ||
be6ce11c PS |
72 | public function test_address_in_subnet() { |
73 | // 1: xxx.xxx.xxx.xxx/nn or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/nnn (number of bits in net mask). | |
a3d5830a PS |
74 | $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.1/32')); |
75 | $this->assertFalse(address_in_subnet('123.121.23.1', '123.121.23.0/32')); | |
76 | $this->assertTrue(address_in_subnet('10.10.10.100', '123.121.23.45/0')); | |
77 | $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/24')); | |
78 | $this->assertFalse(address_in_subnet('123.121.34.1', '123.121.234.0/24')); | |
79 | $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/30')); | |
80 | $this->assertFalse(address_in_subnet('123.121.23.8', '123.121.23.0/30')); | |
81 | $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128')); | |
82 | $this->assertFalse(address_in_subnet('bab:baba::baba', 'bab:baba::cece/128')); | |
83 | $this->assertTrue(address_in_subnet('baba:baba::baba', 'cece:cece::cece/0')); | |
84 | $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128')); | |
85 | $this->assertTrue(address_in_subnet('baba:baba::00ba', 'baba:baba::/120')); | |
86 | $this->assertFalse(address_in_subnet('baba:baba::aba', 'baba:baba::/120')); | |
87 | $this->assertTrue(address_in_subnet('baba::baba:00ba', 'baba::baba:0/112')); | |
88 | $this->assertFalse(address_in_subnet('baba::aba:00ba', 'baba::baba:0/112')); | |
89 | $this->assertFalse(address_in_subnet('aba::baba:0000', 'baba::baba:0/112')); | |
90 | ||
be6ce11c | 91 | // Fixed input. |
a3d5830a PS |
92 | $this->assertTrue(address_in_subnet('123.121.23.1 ', ' 123.121.23.0 / 24')); |
93 | $this->assertTrue(address_in_subnet('::ffff:10.1.1.1', ' 0:0:0:000:0:ffff:a1:10 / 126')); | |
94 | ||
be6ce11c | 95 | // Incorrect input. |
a3d5830a PS |
96 | $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/-2')); |
97 | $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/64')); | |
98 | $this->assertFalse(address_in_subnet('123.121.234.x', '123.121.234.1/24')); | |
99 | $this->assertFalse(address_in_subnet('123.121.234.0', '123.121.234.xx/24')); | |
100 | $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/xx0')); | |
101 | $this->assertFalse(address_in_subnet('::1', '::aa:0/xx0')); | |
102 | $this->assertFalse(address_in_subnet('::1', '::aa:0/-5')); | |
103 | $this->assertFalse(address_in_subnet('::1', '::aa:0/130')); | |
104 | $this->assertFalse(address_in_subnet('x:1', '::aa:0/130')); | |
105 | $this->assertFalse(address_in_subnet('::1', '::ax:0/130')); | |
106 | ||
be6ce11c | 107 | // 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). |
a3d5830a PS |
108 | $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12-14')); |
109 | $this->assertTrue(address_in_subnet('123.121.234.13', '123.121.234.12-14')); | |
110 | $this->assertTrue(address_in_subnet('123.121.234.14', '123.121.234.12-14')); | |
111 | $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.12-14')); | |
112 | $this->assertFalse(address_in_subnet('123.121.234.20', '123.121.234.12-14')); | |
113 | $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.234.12-14')); | |
114 | $this->assertFalse(address_in_subnet('123.12.234.12', '123.121.234.12-14')); | |
115 | $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba-babe')); | |
116 | $this->assertTrue(address_in_subnet('baba:baba::babc', 'baba:baba::baba-babe')); | |
117 | $this->assertTrue(address_in_subnet('baba:baba::babe', 'baba:baba::baba-babe')); | |
118 | $this->assertFalse(address_in_subnet('bab:baba::bab0', 'bab:baba::baba-babe')); | |
119 | $this->assertFalse(address_in_subnet('bab:baba::babf', 'bab:baba::baba-babe')); | |
120 | $this->assertFalse(address_in_subnet('bab:baba::bfbe', 'bab:baba::baba-babe')); | |
121 | $this->assertFalse(address_in_subnet('bfb:baba::babe', 'bab:baba::baba-babe')); | |
122 | ||
be6ce11c | 123 | // Fixed input. |
a3d5830a PS |
124 | $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12 - 14 ')); |
125 | $this->assertTrue(address_in_subnet('bab:baba::babe', 'bab:baba::baba - babe ')); | |
126 | ||
be6ce11c | 127 | // Incorrect input. |
a3d5830a PS |
128 | $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-234.14')); |
129 | $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-256')); | |
130 | $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12--256')); | |
131 | ||
be6ce11c | 132 | // 3: xxx.xxx or xxx.xxx. or xxx:xxx:xxxx or xxx:xxx:xxxx. (incomplete address, a bit non-technical ;-). |
a3d5830a PS |
133 | $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12')); |
134 | $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.23.13')); | |
135 | $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.')); | |
136 | $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234')); | |
137 | $this->assertTrue(address_in_subnet('123.121.234.12', '123.121')); | |
138 | $this->assertTrue(address_in_subnet('123.121.234.12', '123')); | |
139 | $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234.')); | |
140 | $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234')); | |
141 | $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba::bab')); | |
142 | $this->assertFalse(address_in_subnet('baba:baba::ba', 'baba:baba::bc')); | |
143 | $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba')); | |
144 | $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:')); | |
145 | $this->assertFalse(address_in_subnet('bab:baba::bab', 'baba:')); | |
146 | ||
be6ce11c | 147 | // Multiple subnets. |
a3d5830a PS |
148 | $this->assertTrue(address_in_subnet('123.121.234.12', '::1/64, 124., 123.121.234.10-30')); |
149 | $this->assertTrue(address_in_subnet('124.121.234.12', '::1/64, 124., 123.121.234.10-30')); | |
150 | $this->assertTrue(address_in_subnet('::2', '::1/64, 124., 123.121.234.10-30')); | |
151 | $this->assertFalse(address_in_subnet('12.121.234.12', '::1/64, 124., 123.121.234.10-30')); | |
152 | ||
be6ce11c | 153 | // Other incorrect input. |
a3d5830a PS |
154 | $this->assertFalse(address_in_subnet('123.123.123.123', '')); |
155 | } | |
156 | ||
be6ce11c PS |
157 | public function test_fix_utf8() { |
158 | // Make sure valid data including other types is not changed. | |
a3d5830a PS |
159 | $this->assertSame(null, fix_utf8(null)); |
160 | $this->assertSame(1, fix_utf8(1)); | |
161 | $this->assertSame(1.1, fix_utf8(1.1)); | |
162 | $this->assertSame(true, fix_utf8(true)); | |
163 | $this->assertSame('', fix_utf8('')); | |
0aff15c2 | 164 | $this->assertSame('abc', fix_utf8('abc')); |
a3d5830a PS |
165 | $array = array('do', 're', 'mi'); |
166 | $this->assertSame($array, fix_utf8($array)); | |
167 | $object = new stdClass(); | |
168 | $object->a = 'aa'; | |
169 | $object->b = 'bb'; | |
170 | $this->assertEquals($object, fix_utf8($object)); | |
171 | ||
bff1d60c PS |
172 | // valid utf8 string |
173 | $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")); | |
a3d5830a | 174 | |
be6ce11c | 175 | // Invalid utf8 string. |
0aff15c2 | 176 | $this->assertSame('aš', fix_utf8('a'.chr(130).'š'), 'This fails with buggy iconv() when mbstring extenstion is not available as fallback.'); |
a3d5830a PS |
177 | } |
178 | ||
be6ce11c | 179 | public function test_optional_param() { |
a3d5830a PS |
180 | global $CFG; |
181 | ||
182 | $_POST['username'] = 'post_user'; | |
183 | $_GET['username'] = 'get_user'; | |
be6ce11c | 184 | $this->assertSame($_POST['username'], optional_param('username', 'default_user', PARAM_RAW)); |
a3d5830a PS |
185 | |
186 | unset($_POST['username']); | |
be6ce11c | 187 | $this->assertSame($_GET['username'], optional_param('username', 'default_user', PARAM_RAW)); |
a3d5830a PS |
188 | |
189 | unset($_GET['username']); | |
be6ce11c | 190 | $this->assertSame('default_user', optional_param('username', 'default_user', PARAM_RAW)); |
a3d5830a | 191 | |
be6ce11c | 192 | // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2. |
a3d5830a PS |
193 | $_POST['username'] = 'post_user'; |
194 | try { | |
195 | optional_param('username', 'default_user', null); | |
196 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
197 | } catch (moodle_exception $ex) { |
198 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
199 | } |
200 | try { | |
201 | @optional_param('username', 'default_user'); | |
202 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
203 | } catch (moodle_exception $ex) { |
204 | $this->assertInstanceOf('coding_exception', $ex); | |
308ce5b4 MG |
205 | } catch (Error $error) { |
206 | // PHP 7.1 throws Error even earlier. | |
207 | $this->assertRegExp('/Too few arguments to function/', $error->getMessage()); | |
a3d5830a PS |
208 | } |
209 | try { | |
210 | @optional_param('username'); | |
211 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
212 | } catch (moodle_exception $ex) { |
213 | $this->assertInstanceOf('coding_exception', $ex); | |
308ce5b4 MG |
214 | } catch (Error $error) { |
215 | // PHP 7.1 throws Error even earlier. | |
216 | $this->assertRegExp('/Too few arguments to function/', $error->getMessage()); | |
a3d5830a PS |
217 | } |
218 | try { | |
219 | optional_param('', 'default_user', PARAM_RAW); | |
220 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
221 | } catch (moodle_exception $ex) { |
222 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
223 | } |
224 | ||
be6ce11c | 225 | // Make sure warning is displayed if array submitted - TODO: throw exception in Moodle 2.3. |
c8bc6c15 | 226 | $_POST['username'] = array('a'=>'a'); |
be6ce11c | 227 | $this->assertSame($_POST['username'], optional_param('username', 'default_user', PARAM_RAW)); |
c8bc6c15 | 228 | $this->assertDebuggingCalled(); |
a3d5830a PS |
229 | } |
230 | ||
be6ce11c | 231 | public function test_optional_param_array() { |
a3d5830a PS |
232 | global $CFG; |
233 | ||
234 | $_POST['username'] = array('a'=>'post_user'); | |
235 | $_GET['username'] = array('a'=>'get_user'); | |
be6ce11c | 236 | $this->assertSame($_POST['username'], optional_param_array('username', array('a'=>'default_user'), PARAM_RAW)); |
a3d5830a PS |
237 | |
238 | unset($_POST['username']); | |
be6ce11c | 239 | $this->assertSame($_GET['username'], optional_param_array('username', array('a'=>'default_user'), PARAM_RAW)); |
a3d5830a PS |
240 | |
241 | unset($_GET['username']); | |
be6ce11c | 242 | $this->assertSame(array('a'=>'default_user'), optional_param_array('username', array('a'=>'default_user'), PARAM_RAW)); |
a3d5830a | 243 | |
be6ce11c | 244 | // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2. |
a3d5830a PS |
245 | $_POST['username'] = array('a'=>'post_user'); |
246 | try { | |
247 | optional_param_array('username', array('a'=>'default_user'), null); | |
248 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
249 | } catch (moodle_exception $ex) { |
250 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
251 | } |
252 | try { | |
253 | @optional_param_array('username', array('a'=>'default_user')); | |
254 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
255 | } catch (moodle_exception $ex) { |
256 | $this->assertInstanceOf('coding_exception', $ex); | |
308ce5b4 MG |
257 | } catch (Error $error) { |
258 | // PHP 7.1 throws Error even earlier. | |
259 | $this->assertRegExp('/Too few arguments to function/', $error->getMessage()); | |
a3d5830a PS |
260 | } |
261 | try { | |
262 | @optional_param_array('username'); | |
263 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
264 | } catch (moodle_exception $ex) { |
265 | $this->assertInstanceOf('coding_exception', $ex); | |
308ce5b4 MG |
266 | } catch (Error $error) { |
267 | // PHP 7.1 throws Error even earlier. | |
268 | $this->assertRegExp('/Too few arguments to function/', $error->getMessage()); | |
a3d5830a PS |
269 | } |
270 | try { | |
271 | optional_param_array('', array('a'=>'default_user'), PARAM_RAW); | |
272 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
273 | } catch (moodle_exception $ex) { |
274 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
275 | } |
276 | ||
be6ce11c | 277 | // Do not allow nested arrays. |
a3d5830a PS |
278 | try { |
279 | $_POST['username'] = array('a'=>array('b'=>'post_user')); | |
280 | optional_param_array('username', array('a'=>'default_user'), PARAM_RAW); | |
281 | $this->fail('coding_exception expected'); | |
282 | } catch (coding_exception $ex) { | |
283 | $this->assertTrue(true); | |
284 | } | |
285 | ||
be6ce11c | 286 | // Do not allow non-arrays. |
a3d5830a | 287 | $_POST['username'] = 'post_user'; |
be6ce11c | 288 | $this->assertSame(array('a'=>'default_user'), optional_param_array('username', array('a'=>'default_user'), PARAM_RAW)); |
c8bc6c15 | 289 | $this->assertDebuggingCalled(); |
a3d5830a | 290 | |
be6ce11c | 291 | // Make sure array keys are sanitised. |
a3d5830a | 292 | $_POST['username'] = array('abc123_;-/*-+ '=>'arrggh', 'a1_-'=>'post_user'); |
be6ce11c | 293 | $this->assertSame(array('a1_-'=>'post_user'), optional_param_array('username', array(), PARAM_RAW)); |
c8bc6c15 | 294 | $this->assertDebuggingCalled(); |
a3d5830a PS |
295 | } |
296 | ||
be6ce11c | 297 | public function test_required_param() { |
a3d5830a PS |
298 | $_POST['username'] = 'post_user'; |
299 | $_GET['username'] = 'get_user'; | |
be6ce11c | 300 | $this->assertSame('post_user', required_param('username', PARAM_RAW)); |
a3d5830a PS |
301 | |
302 | unset($_POST['username']); | |
be6ce11c | 303 | $this->assertSame('get_user', required_param('username', PARAM_RAW)); |
a3d5830a PS |
304 | |
305 | unset($_GET['username']); | |
306 | try { | |
be6ce11c | 307 | $this->assertSame('default_user', required_param('username', PARAM_RAW)); |
a3d5830a PS |
308 | $this->fail('moodle_exception expected'); |
309 | } catch (moodle_exception $ex) { | |
be6ce11c | 310 | $this->assertInstanceOf('moodle_exception', $ex); |
a3d5830a PS |
311 | } |
312 | ||
be6ce11c | 313 | // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2. |
a3d5830a PS |
314 | $_POST['username'] = 'post_user'; |
315 | try { | |
316 | @required_param('username'); | |
317 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
318 | } catch (moodle_exception $ex) { |
319 | $this->assertInstanceOf('coding_exception', $ex); | |
308ce5b4 MG |
320 | } catch (Error $error) { |
321 | // PHP 7.1 throws Error even earlier. | |
322 | $this->assertRegExp('/Too few arguments to function/', $error->getMessage()); | |
a3d5830a PS |
323 | } |
324 | try { | |
325 | required_param('username', ''); | |
326 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
327 | } catch (moodle_exception $ex) { |
328 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
329 | } |
330 | try { | |
331 | required_param('', PARAM_RAW); | |
332 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
333 | } catch (moodle_exception $ex) { |
334 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
335 | } |
336 | ||
be6ce11c | 337 | // Make sure warning is displayed if array submitted - TODO: throw exception in Moodle 2.3. |
c8bc6c15 | 338 | $_POST['username'] = array('a'=>'a'); |
be6ce11c | 339 | $this->assertSame($_POST['username'], required_param('username', PARAM_RAW)); |
c8bc6c15 | 340 | $this->assertDebuggingCalled(); |
a3d5830a PS |
341 | } |
342 | ||
be6ce11c | 343 | public function test_required_param_array() { |
a3d5830a PS |
344 | global $CFG; |
345 | ||
346 | $_POST['username'] = array('a'=>'post_user'); | |
347 | $_GET['username'] = array('a'=>'get_user'); | |
be6ce11c | 348 | $this->assertSame($_POST['username'], required_param_array('username', PARAM_RAW)); |
a3d5830a PS |
349 | |
350 | unset($_POST['username']); | |
be6ce11c | 351 | $this->assertSame($_GET['username'], required_param_array('username', PARAM_RAW)); |
a3d5830a | 352 | |
be6ce11c | 353 | // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2. |
a3d5830a PS |
354 | $_POST['username'] = array('a'=>'post_user'); |
355 | try { | |
356 | required_param_array('username', null); | |
357 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
358 | } catch (moodle_exception $ex) { |
359 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
360 | } |
361 | try { | |
362 | @required_param_array('username'); | |
363 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
364 | } catch (moodle_exception $ex) { |
365 | $this->assertInstanceOf('coding_exception', $ex); | |
308ce5b4 MG |
366 | } catch (Error $error) { |
367 | // PHP 7.1 throws Error. | |
368 | $this->assertRegExp('/Too few arguments to function/', $error->getMessage()); | |
a3d5830a PS |
369 | } |
370 | try { | |
371 | required_param_array('', PARAM_RAW); | |
372 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
373 | } catch (moodle_exception $ex) { |
374 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
375 | } |
376 | ||
be6ce11c | 377 | // Do not allow nested arrays. |
a3d5830a PS |
378 | try { |
379 | $_POST['username'] = array('a'=>array('b'=>'post_user')); | |
380 | required_param_array('username', PARAM_RAW); | |
381 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
382 | } catch (moodle_exception $ex) { |
383 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
384 | } |
385 | ||
be6ce11c | 386 | // Do not allow non-arrays. |
a3d5830a PS |
387 | try { |
388 | $_POST['username'] = 'post_user'; | |
389 | required_param_array('username', PARAM_RAW); | |
390 | $this->fail('moodle_exception expected'); | |
391 | } catch (moodle_exception $ex) { | |
be6ce11c | 392 | $this->assertInstanceOf('moodle_exception', $ex); |
a3d5830a PS |
393 | } |
394 | ||
be6ce11c | 395 | // Make sure array keys are sanitised. |
a3d5830a | 396 | $_POST['username'] = array('abc123_;-/*-+ '=>'arrggh', 'a1_-'=>'post_user'); |
be6ce11c | 397 | $this->assertSame(array('a1_-'=>'post_user'), required_param_array('username', PARAM_RAW)); |
c8bc6c15 | 398 | $this->assertDebuggingCalled(); |
a3d5830a PS |
399 | } |
400 | ||
be6ce11c PS |
401 | public function test_clean_param() { |
402 | // Forbid objects and arrays. | |
a3d5830a PS |
403 | try { |
404 | clean_param(array('x', 'y'), PARAM_RAW); | |
405 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
406 | } catch (moodle_exception $ex) { |
407 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
408 | } |
409 | try { | |
410 | $param = new stdClass(); | |
411 | $param->id = 1; | |
412 | clean_param($param, PARAM_RAW); | |
413 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
414 | } catch (moodle_exception $ex) { |
415 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
416 | } |
417 | ||
be6ce11c | 418 | // Require correct type. |
a3d5830a PS |
419 | try { |
420 | clean_param('x', 'xxxxxx'); | |
421 | $this->fail('moodle_exception expected'); | |
422 | } catch (moodle_exception $ex) { | |
be6ce11c | 423 | $this->assertInstanceOf('moodle_exception', $ex); |
a3d5830a PS |
424 | } |
425 | try { | |
426 | @clean_param('x'); | |
427 | $this->fail('moodle_exception expected'); | |
428 | } catch (moodle_exception $ex) { | |
be6ce11c | 429 | $this->assertInstanceOf('moodle_exception', $ex); |
308ce5b4 MG |
430 | } catch (Error $error) { |
431 | // PHP 7.1 throws Error even earlier. | |
432 | $this->assertRegExp('/Too few arguments to function/', $error->getMessage()); | |
a3d5830a | 433 | } |
a3d5830a PS |
434 | } |
435 | ||
be6ce11c PS |
436 | public function test_clean_param_array() { |
437 | $this->assertSame(array(), clean_param_array(null, PARAM_RAW)); | |
438 | $this->assertSame(array('a', 'b'), clean_param_array(array('a', 'b'), PARAM_RAW)); | |
439 | $this->assertSame(array('a', array('b')), clean_param_array(array('a', array('b')), PARAM_RAW, true)); | |
a3d5830a | 440 | |
be6ce11c | 441 | // Require correct type. |
a3d5830a PS |
442 | try { |
443 | clean_param_array(array('x'), 'xxxxxx'); | |
444 | $this->fail('moodle_exception expected'); | |
445 | } catch (moodle_exception $ex) { | |
be6ce11c | 446 | $this->assertInstanceOf('moodle_exception', $ex); |
a3d5830a PS |
447 | } |
448 | try { | |
449 | @clean_param_array(array('x')); | |
450 | $this->fail('moodle_exception expected'); | |
451 | } catch (moodle_exception $ex) { | |
be6ce11c | 452 | $this->assertInstanceOf('moodle_exception', $ex); |
308ce5b4 MG |
453 | } catch (Error $error) { |
454 | // PHP 7.1 throws Error even earlier. | |
455 | $this->assertRegExp('/Too few arguments to function/', $error->getMessage()); | |
a3d5830a PS |
456 | } |
457 | ||
458 | try { | |
459 | clean_param_array(array('x', array('y')), PARAM_RAW); | |
460 | $this->fail('coding_exception expected'); | |
be6ce11c PS |
461 | } catch (moodle_exception $ex) { |
462 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
463 | } |
464 | ||
be6ce11c | 465 | // Test recursive. |
a3d5830a PS |
466 | } |
467 | ||
be6ce11c PS |
468 | public function test_clean_param_raw() { |
469 | $this->assertSame( | |
470 | '#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', | |
471 | clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_RAW)); | |
a3d5830a PS |
472 | } |
473 | ||
be6ce11c PS |
474 | public function test_clean_param_trim() { |
475 | $this->assertSame('Frog toad', clean_param(" Frog toad \r\n ", PARAM_RAW_TRIMMED)); | |
a3d5830a PS |
476 | } |
477 | ||
be6ce11c PS |
478 | public function test_clean_param_clean() { |
479 | // PARAM_CLEAN is an ugly hack, do not use in new code (skodak), | |
480 | // instead use more specific type, or submit sothing that can be verified properly. | |
481 | $this->assertSame('xx', clean_param('xx<script>', PARAM_CLEAN)); | |
a3d5830a PS |
482 | } |
483 | ||
be6ce11c PS |
484 | public function test_clean_param_alpha() { |
485 | $this->assertSame('DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHA)); | |
a3d5830a PS |
486 | } |
487 | ||
be6ce11c PS |
488 | public function test_clean_param_alphanum() { |
489 | $this->assertSame('978942897DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHANUM)); | |
a3d5830a PS |
490 | } |
491 | ||
be6ce11c PS |
492 | public function test_clean_param_alphaext() { |
493 | $this->assertSame('DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHAEXT)); | |
a3d5830a PS |
494 | } |
495 | ||
be6ce11c PS |
496 | public function test_clean_param_sequence() { |
497 | $this->assertSame(',9789,42897', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_SEQUENCE)); | |
a3d5830a PS |
498 | } |
499 | ||
be6ce11c PS |
500 | public function test_clean_param_component() { |
501 | // Please note the cleaning of component names is very strict, no guessing here. | |
502 | $this->assertSame('mod_forum', clean_param('mod_forum', PARAM_COMPONENT)); | |
503 | $this->assertSame('block_online_users', clean_param('block_online_users', PARAM_COMPONENT)); | |
504 | $this->assertSame('block_blond_online_users', clean_param('block_blond_online_users', PARAM_COMPONENT)); | |
505 | $this->assertSame('mod_something2', clean_param('mod_something2', PARAM_COMPONENT)); | |
506 | $this->assertSame('forum', clean_param('forum', PARAM_COMPONENT)); | |
507 | $this->assertSame('user', clean_param('user', PARAM_COMPONENT)); | |
508 | $this->assertSame('rating', clean_param('rating', PARAM_COMPONENT)); | |
a41d1ca0 PS |
509 | $this->assertSame('feedback360', clean_param('feedback360', PARAM_COMPONENT)); |
510 | $this->assertSame('mod_feedback360', clean_param('mod_feedback360', PARAM_COMPONENT)); | |
be6ce11c PS |
511 | $this->assertSame('', clean_param('mod_2something', PARAM_COMPONENT)); |
512 | $this->assertSame('', clean_param('2mod_something', PARAM_COMPONENT)); | |
513 | $this->assertSame('', clean_param('mod_something_xx', PARAM_COMPONENT)); | |
514 | $this->assertSame('', clean_param('auth_something__xx', PARAM_COMPONENT)); | |
515 | $this->assertSame('', clean_param('mod_Something', PARAM_COMPONENT)); | |
516 | $this->assertSame('', clean_param('mod_somethíng', PARAM_COMPONENT)); | |
b5486ce8 | 517 | $this->assertSame('', clean_param('mod__something', PARAM_COMPONENT)); |
be6ce11c PS |
518 | $this->assertSame('', clean_param('auth_xx-yy', PARAM_COMPONENT)); |
519 | $this->assertSame('', clean_param('_auth_xx', PARAM_COMPONENT)); | |
520 | $this->assertSame('', clean_param('a2uth_xx', PARAM_COMPONENT)); | |
521 | $this->assertSame('', clean_param('auth_xx_', PARAM_COMPONENT)); | |
522 | $this->assertSame('', clean_param('auth_xx.old', PARAM_COMPONENT)); | |
523 | $this->assertSame('', clean_param('_user', PARAM_COMPONENT)); | |
524 | $this->assertSame('', clean_param('2rating', PARAM_COMPONENT)); | |
525 | $this->assertSame('', clean_param('user_', PARAM_COMPONENT)); | |
a3d5830a PS |
526 | } |
527 | ||
be6ce11c | 528 | public function test_is_valid_plugin_name() { |
5f850735 TH |
529 | $this->assertTrue(is_valid_plugin_name('forum')); |
530 | $this->assertTrue(is_valid_plugin_name('forum2')); | |
a41d1ca0 | 531 | $this->assertTrue(is_valid_plugin_name('feedback360')); |
5f850735 TH |
532 | $this->assertTrue(is_valid_plugin_name('online_users')); |
533 | $this->assertTrue(is_valid_plugin_name('blond_online_users')); | |
534 | $this->assertFalse(is_valid_plugin_name('online__users')); | |
535 | $this->assertFalse(is_valid_plugin_name('forum ')); | |
536 | $this->assertFalse(is_valid_plugin_name('forum.old')); | |
537 | $this->assertFalse(is_valid_plugin_name('xx-yy')); | |
538 | $this->assertFalse(is_valid_plugin_name('2xx')); | |
539 | $this->assertFalse(is_valid_plugin_name('Xx')); | |
540 | $this->assertFalse(is_valid_plugin_name('_xx')); | |
541 | $this->assertFalse(is_valid_plugin_name('xx_')); | |
542 | } | |
543 | ||
be6ce11c PS |
544 | public function test_clean_param_plugin() { |
545 | // Please note the cleaning of plugin names is very strict, no guessing here. | |
546 | $this->assertSame('forum', clean_param('forum', PARAM_PLUGIN)); | |
547 | $this->assertSame('forum2', clean_param('forum2', PARAM_PLUGIN)); | |
a41d1ca0 | 548 | $this->assertSame('feedback360', clean_param('feedback360', PARAM_PLUGIN)); |
be6ce11c PS |
549 | $this->assertSame('online_users', clean_param('online_users', PARAM_PLUGIN)); |
550 | $this->assertSame('blond_online_users', clean_param('blond_online_users', PARAM_PLUGIN)); | |
551 | $this->assertSame('', clean_param('online__users', PARAM_PLUGIN)); | |
552 | $this->assertSame('', clean_param('forum ', PARAM_PLUGIN)); | |
553 | $this->assertSame('', clean_param('forum.old', PARAM_PLUGIN)); | |
554 | $this->assertSame('', clean_param('xx-yy', PARAM_PLUGIN)); | |
555 | $this->assertSame('', clean_param('2xx', PARAM_PLUGIN)); | |
556 | $this->assertSame('', clean_param('Xx', PARAM_PLUGIN)); | |
557 | $this->assertSame('', clean_param('_xx', PARAM_PLUGIN)); | |
558 | $this->assertSame('', clean_param('xx_', PARAM_PLUGIN)); | |
559 | } | |
560 | ||
561 | public function test_clean_param_area() { | |
562 | // Please note the cleaning of area names is very strict, no guessing here. | |
563 | $this->assertSame('something', clean_param('something', PARAM_AREA)); | |
564 | $this->assertSame('something2', clean_param('something2', PARAM_AREA)); | |
565 | $this->assertSame('some_thing', clean_param('some_thing', PARAM_AREA)); | |
566 | $this->assertSame('some_thing_xx', clean_param('some_thing_xx', PARAM_AREA)); | |
a41d1ca0 | 567 | $this->assertSame('feedback360', clean_param('feedback360', PARAM_AREA)); |
be6ce11c PS |
568 | $this->assertSame('', clean_param('_something', PARAM_AREA)); |
569 | $this->assertSame('', clean_param('something_', PARAM_AREA)); | |
570 | $this->assertSame('', clean_param('2something', PARAM_AREA)); | |
571 | $this->assertSame('', clean_param('Something', PARAM_AREA)); | |
572 | $this->assertSame('', clean_param('some-thing', PARAM_AREA)); | |
573 | $this->assertSame('', clean_param('somethííng', PARAM_AREA)); | |
574 | $this->assertSame('', clean_param('something.x', PARAM_AREA)); | |
575 | } | |
576 | ||
577 | public function test_clean_param_text() { | |
578 | $this->assertSame(PARAM_TEXT, PARAM_MULTILANG); | |
579 | // Standard. | |
580 | $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)); | |
581 | $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)); | |
582 | $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)); | |
583 | // Malformed. | |
584 | $this->assertSame('<span lang="en" class="multilang">aa</span>', clean_param('<span lang="en" class="multilang">aa</span>', PARAM_TEXT)); | |
585 | $this->assertSame('aa', clean_param('<span lang="en" class="nothing" class="multilang">aa</span>', PARAM_TEXT)); | |
586 | $this->assertSame('aa', clean_param('<lang lang="en" class="multilang">aa</lang>', PARAM_TEXT)); | |
587 | $this->assertSame('aa', clean_param('<lang lang="en!!">aa</lang>', PARAM_TEXT)); | |
588 | $this->assertSame('aa', clean_param('<span lang="en==" class="multilang">aa</span>', PARAM_TEXT)); | |
589 | $this->assertSame('abc', clean_param('a<em>b</em>c', PARAM_TEXT)); | |
590 | $this->assertSame('a>c>', clean_param('a><xx >c>', PARAM_TEXT)); // Standard strip_tags() behaviour. | |
591 | $this->assertSame('a', clean_param('a<b', PARAM_TEXT)); | |
592 | $this->assertSame('a>b', clean_param('a>b', PARAM_TEXT)); | |
593 | $this->assertSame('<lang lang="en">a>a</lang>', clean_param('<lang lang="en">a>a</lang>', PARAM_TEXT)); // Standard strip_tags() behaviour. | |
594 | $this->assertSame('a', clean_param('<lang lang="en">a<a</lang>', PARAM_TEXT)); | |
595 | $this->assertSame('<lang lang="en">aa</lang>', clean_param('<lang lang="en">a<br>a</lang>', PARAM_TEXT)); | |
596 | } | |
597 | ||
598 | public function test_clean_param_url() { | |
599 | // Test PARAM_URL and PARAM_LOCALURL a bit. | |
600 | $this->assertSame('http://google.com/', clean_param('http://google.com/', PARAM_URL)); | |
601 | $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)); | |
602 | $this->assertSame('http://localhost/', clean_param('http://localhost/', PARAM_URL)); | |
603 | $this->assertSame('http://0.255.1.1/numericip.php', clean_param('http://0.255.1.1/numericip.php', PARAM_URL)); | |
604 | $this->assertSame('/just/a/path', clean_param('/just/a/path', PARAM_URL)); | |
605 | $this->assertSame('', clean_param('funny:thing', PARAM_URL)); | |
606 | } | |
607 | ||
608 | public function test_clean_param_localurl() { | |
a3d5830a | 609 | global $CFG; |
0339f2c4 EL |
610 | |
611 | $this->resetAfterTest(); | |
612 | ||
d031c2dc FM |
613 | // External, invalid. |
614 | $this->assertSame('', clean_param('funny:thing', PARAM_LOCALURL)); | |
be6ce11c | 615 | $this->assertSame('', clean_param('http://google.com/', PARAM_LOCALURL)); |
d031c2dc | 616 | $this->assertSame('', clean_param('https://google.com/?test=true', PARAM_LOCALURL)); |
be6ce11c | 617 | $this->assertSame('', clean_param('http://some.very.long.and.silly.domain/with/a/path/', PARAM_LOCALURL)); |
d031c2dc FM |
618 | |
619 | // Local absolute. | |
be6ce11c | 620 | $this->assertSame(clean_param($CFG->wwwroot, PARAM_LOCALURL), $CFG->wwwroot); |
d031c2dc FM |
621 | $this->assertSame($CFG->wwwroot . '/with/something?else=true', |
622 | clean_param($CFG->wwwroot . '/with/something?else=true', PARAM_LOCALURL)); | |
623 | ||
624 | // Local relative. | |
be6ce11c | 625 | $this->assertSame('/just/a/path', clean_param('/just/a/path', PARAM_LOCALURL)); |
be6ce11c | 626 | $this->assertSame('course/view.php?id=3', clean_param('course/view.php?id=3', PARAM_LOCALURL)); |
d031c2dc FM |
627 | |
628 | // Local absolute HTTPS. | |
629 | $httpsroot = str_replace('http:', 'https:', $CFG->wwwroot); | |
d031c2dc FM |
630 | $CFG->loginhttps = false; |
631 | $this->assertSame('', clean_param($httpsroot, PARAM_LOCALURL)); | |
632 | $this->assertSame('', clean_param($httpsroot . '/with/something?else=true', PARAM_LOCALURL)); | |
633 | $CFG->loginhttps = true; | |
634 | $this->assertSame($httpsroot, clean_param($httpsroot, PARAM_LOCALURL)); | |
635 | $this->assertSame($httpsroot . '/with/something?else=true', | |
636 | clean_param($httpsroot . '/with/something?else=true', PARAM_LOCALURL)); | |
0339f2c4 EL |
637 | |
638 | // Test open redirects are not possible. | |
639 | $CFG->loginhttps = false; | |
640 | $CFG->wwwroot = 'http://www.example.com'; | |
641 | $this->assertSame('', clean_param('http://www.example.com.evil.net/hack.php', PARAM_LOCALURL)); | |
642 | $CFG->loginhttps = true; | |
643 | $this->assertSame('', clean_param('https://www.example.com.evil.net/hack.php', PARAM_LOCALURL)); | |
be6ce11c PS |
644 | } |
645 | ||
646 | public function test_clean_param_file() { | |
647 | $this->assertSame('correctfile.txt', clean_param('correctfile.txt', PARAM_FILE)); | |
648 | $this->assertSame('badfile.txt', clean_param('b\'a<d`\\/fi:l>e.t"x|t', PARAM_FILE)); | |
649 | $this->assertSame('..parentdirfile.txt', clean_param('../parentdirfile.txt', PARAM_FILE)); | |
650 | $this->assertSame('....grandparentdirfile.txt', clean_param('../../grandparentdirfile.txt', PARAM_FILE)); | |
651 | $this->assertSame('..winparentdirfile.txt', clean_param('..\winparentdirfile.txt', PARAM_FILE)); | |
652 | $this->assertSame('....wingrandparentdir.txt', clean_param('..\..\wingrandparentdir.txt', PARAM_FILE)); | |
653 | $this->assertSame('myfile.a.b.txt', clean_param('myfile.a.b.txt', PARAM_FILE)); | |
654 | $this->assertSame('myfile..a..b.txt', clean_param('myfile..a..b.txt', PARAM_FILE)); | |
655 | $this->assertSame('myfile.a..b...txt', clean_param('myfile.a..b...txt', PARAM_FILE)); | |
656 | $this->assertSame('myfile.a.txt', clean_param('myfile.a.txt', PARAM_FILE)); | |
657 | $this->assertSame('myfile...txt', clean_param('myfile...txt', PARAM_FILE)); | |
658 | $this->assertSame('...jpg', clean_param('...jpg', PARAM_FILE)); | |
659 | $this->assertSame('.a.b.', clean_param('.a.b.', PARAM_FILE)); | |
660 | $this->assertSame('', clean_param('.', PARAM_FILE)); | |
661 | $this->assertSame('', clean_param('..', PARAM_FILE)); | |
662 | $this->assertSame('...', clean_param('...', PARAM_FILE)); | |
663 | $this->assertSame('. . . .', clean_param('. . . .', PARAM_FILE)); | |
664 | $this->assertSame('dontrtrim.me. .. .. . ', clean_param('dontrtrim.me. .. .. . ', PARAM_FILE)); | |
665 | $this->assertSame(' . .dontltrim.me', clean_param(' . .dontltrim.me', PARAM_FILE)); | |
666 | $this->assertSame('here is a tab.txt', clean_param("here is a tab\t.txt", PARAM_FILE)); | |
667 | $this->assertSame('here is a linebreak.txt', clean_param("here is a line\r\nbreak.txt", PARAM_FILE)); | |
668 | ||
669 | // The following behaviours have been maintained although they seem a little odd. | |
670 | $this->assertSame('funnything', clean_param('funny:thing', PARAM_FILE)); | |
671 | $this->assertSame('.currentdirfile.txt', clean_param('./currentdirfile.txt', PARAM_FILE)); | |
672 | $this->assertSame('ctempwindowsfile.txt', clean_param('c:\temp\windowsfile.txt', PARAM_FILE)); | |
673 | $this->assertSame('homeuserlinuxfile.txt', clean_param('/home/user/linuxfile.txt', PARAM_FILE)); | |
674 | $this->assertSame('~myfile.txt', clean_param('~/myfile.txt', PARAM_FILE)); | |
675 | } | |
676 | ||
677 | public function test_clean_param_path() { | |
678 | $this->assertSame('correctfile.txt', clean_param('correctfile.txt', PARAM_PATH)); | |
679 | $this->assertSame('bad/file.txt', clean_param('b\'a<d`\\/fi:l>e.t"x|t', PARAM_PATH)); | |
680 | $this->assertSame('/parentdirfile.txt', clean_param('../parentdirfile.txt', PARAM_PATH)); | |
681 | $this->assertSame('/grandparentdirfile.txt', clean_param('../../grandparentdirfile.txt', PARAM_PATH)); | |
682 | $this->assertSame('/winparentdirfile.txt', clean_param('..\winparentdirfile.txt', PARAM_PATH)); | |
683 | $this->assertSame('/wingrandparentdir.txt', clean_param('..\..\wingrandparentdir.txt', PARAM_PATH)); | |
684 | $this->assertSame('funnything', clean_param('funny:thing', PARAM_PATH)); | |
685 | $this->assertSame('./here', clean_param('./././here', PARAM_PATH)); | |
686 | $this->assertSame('./currentdirfile.txt', clean_param('./currentdirfile.txt', PARAM_PATH)); | |
687 | $this->assertSame('c/temp/windowsfile.txt', clean_param('c:\temp\windowsfile.txt', PARAM_PATH)); | |
688 | $this->assertSame('/home/user/linuxfile.txt', clean_param('/home/user/linuxfile.txt', PARAM_PATH)); | |
689 | $this->assertSame('/home../user ./.linuxfile.txt', clean_param('/home../user ./.linuxfile.txt', PARAM_PATH)); | |
690 | $this->assertSame('~/myfile.txt', clean_param('~/myfile.txt', PARAM_PATH)); | |
691 | $this->assertSame('~/myfile.txt', clean_param('~/../myfile.txt', PARAM_PATH)); | |
692 | $this->assertSame('/..b../.../myfile.txt', clean_param('/..b../.../myfile.txt', PARAM_PATH)); | |
693 | $this->assertSame('..b../.../myfile.txt', clean_param('..b../.../myfile.txt', PARAM_PATH)); | |
694 | $this->assertSame('/super/slashes/', clean_param('/super//slashes///', PARAM_PATH)); | |
695 | } | |
696 | ||
697 | public function test_clean_param_username() { | |
a3d5830a PS |
698 | global $CFG; |
699 | $currentstatus = $CFG->extendedusernamechars; | |
700 | ||
be6ce11c PS |
701 | // Run tests with extended character == false;. |
702 | $CFG->extendedusernamechars = false; | |
703 | $this->assertSame('johndoe123', clean_param('johndoe123', PARAM_USERNAME) ); | |
704 | $this->assertSame('john.doe', clean_param('john.doe', PARAM_USERNAME)); | |
705 | $this->assertSame('john-doe', clean_param('john-doe', PARAM_USERNAME)); | |
706 | $this->assertSame('john-doe', clean_param('john- doe', PARAM_USERNAME)); | |
707 | $this->assertSame('john_doe', clean_param('john_doe', PARAM_USERNAME)); | |
708 | $this->assertSame('john@doe', clean_param('john@doe', PARAM_USERNAME)); | |
709 | $this->assertSame('johndoe', clean_param('john~doe', PARAM_USERNAME)); | |
710 | $this->assertSame('johndoe', clean_param('john´doe', PARAM_USERNAME)); | |
77218e4a | 711 | $this->assertSame(clean_param('john# $%&()+_^', PARAM_USERNAME), 'john_'); |
712 | $this->assertSame(clean_param(' john# $%&()+_^ ', PARAM_USERNAME), 'john_'); | |
be6ce11c PS |
713 | $this->assertSame(clean_param('john#$%&() ', PARAM_USERNAME), 'john'); |
714 | $this->assertSame('johnd', clean_param('JOHNdóé ', PARAM_USERNAME)); | |
715 | $this->assertSame(clean_param('john.,:;-_/|\ñÑ[]A_X-,D {} ~!@#$%^&*()_+ ?><[] ščřžžý ?ýá\9e?\9eý??\9adoe ', PARAM_USERNAME), 'john.-_a_x-d@_doe'); | |
716 | ||
717 | // Test success condition, if extendedusernamechars == ENABLE;. | |
718 | $CFG->extendedusernamechars = true; | |
719 | $this->assertSame('john_doe', clean_param('john_doe', PARAM_USERNAME)); | |
720 | $this->assertSame('john@doe', clean_param('john@doe', PARAM_USERNAME)); | |
77218e4a | 721 | $this->assertSame(clean_param('john# $%&()+_^', PARAM_USERNAME), 'john# $%&()+_^'); |
722 | $this->assertSame(clean_param(' john# $%&()+_^ ', PARAM_USERNAME), 'john# $%&()+_^'); | |
be6ce11c PS |
723 | $this->assertSame('john~doe', clean_param('john~doe', PARAM_USERNAME)); |
724 | $this->assertSame('john´doe', clean_param('joHN´doe', PARAM_USERNAME)); | |
725 | $this->assertSame('johndoe', clean_param('johnDOE', PARAM_USERNAME)); | |
726 | $this->assertSame('johndóé', clean_param('johndóé ', PARAM_USERNAME)); | |
a3d5830a PS |
727 | |
728 | $CFG->extendedusernamechars = $currentstatus; | |
729 | } | |
730 | ||
be6ce11c PS |
731 | public function test_clean_param_stringid() { |
732 | // Test string identifiers validation. | |
733 | // Valid strings. | |
734 | $this->assertSame('validstring', clean_param('validstring', PARAM_STRINGID)); | |
735 | $this->assertSame('mod/foobar:valid_capability', clean_param('mod/foobar:valid_capability', PARAM_STRINGID)); | |
736 | $this->assertSame('CZ', clean_param('CZ', PARAM_STRINGID)); | |
737 | $this->assertSame('application/vnd.ms-powerpoint', clean_param('application/vnd.ms-powerpoint', PARAM_STRINGID)); | |
738 | $this->assertSame('grade2', clean_param('grade2', PARAM_STRINGID)); | |
739 | // Invalid strings. | |
740 | $this->assertSame('', clean_param('trailing ', PARAM_STRINGID)); | |
741 | $this->assertSame('', clean_param('space bar', PARAM_STRINGID)); | |
742 | $this->assertSame('', clean_param('0numeric', PARAM_STRINGID)); | |
743 | $this->assertSame('', clean_param('*', PARAM_STRINGID)); | |
744 | $this->assertSame('', clean_param(' ', PARAM_STRINGID)); | |
745 | } | |
746 | ||
747 | public function test_clean_param_timezone() { | |
748 | // Test timezone validation. | |
a3d5830a PS |
749 | $testvalues = array ( |
750 | 'America/Jamaica' => 'America/Jamaica', | |
751 | 'America/Argentina/Cordoba' => 'America/Argentina/Cordoba', | |
752 | 'America/Port-au-Prince' => 'America/Port-au-Prince', | |
753 | 'America/Argentina/Buenos_Aires' => 'America/Argentina/Buenos_Aires', | |
754 | 'PST8PDT' => 'PST8PDT', | |
755 | 'Wrong.Value' => '', | |
756 | 'Wrong/.Value' => '', | |
757 | 'Wrong(Value)' => '', | |
758 | '0' => '0', | |
759 | '0.0' => '0.0', | |
760 | '0.5' => '0.5', | |
8158ce79 JM |
761 | '9.0' => '9.0', |
762 | '-9.0' => '-9.0', | |
763 | '+9.0' => '+9.0', | |
764 | '9.5' => '9.5', | |
765 | '-9.5' => '-9.5', | |
766 | '+9.5' => '+9.5', | |
767 | '12.0' => '12.0', | |
768 | '-12.0' => '-12.0', | |
769 | '+12.0' => '+12.0', | |
770 | '12.5' => '12.5', | |
a3d5830a PS |
771 | '-12.5' => '-12.5', |
772 | '+12.5' => '+12.5', | |
8158ce79 JM |
773 | '13.0' => '13.0', |
774 | '-13.0' => '-13.0', | |
775 | '+13.0' => '+13.0', | |
a3d5830a | 776 | '13.5' => '', |
8158ce79 | 777 | '+13.5' => '', |
a3d5830a PS |
778 | '-13.5' => '', |
779 | '0.2' => ''); | |
780 | ||
781 | foreach ($testvalues as $testvalue => $expectedvalue) { | |
782 | $actualvalue = clean_param($testvalue, PARAM_TIMEZONE); | |
be6ce11c | 783 | $this->assertEquals($expectedvalue, $actualvalue); |
a3d5830a PS |
784 | } |
785 | } | |
786 | ||
be6ce11c | 787 | public function test_validate_param() { |
a3d5830a PS |
788 | try { |
789 | $param = validate_param('11a', PARAM_INT); | |
790 | $this->fail('invalid_parameter_exception expected'); | |
be6ce11c PS |
791 | } catch (moodle_exception $ex) { |
792 | $this->assertInstanceOf('invalid_parameter_exception', $ex); | |
a3d5830a | 793 | } |
be6ce11c PS |
794 | |
795 | $param = validate_param('11', PARAM_INT); | |
796 | $this->assertSame(11, $param); | |
797 | ||
a3d5830a PS |
798 | try { |
799 | $param = validate_param(null, PARAM_INT, false); | |
800 | $this->fail('invalid_parameter_exception expected'); | |
be6ce11c PS |
801 | } catch (moodle_exception $ex) { |
802 | $this->assertInstanceOf('invalid_parameter_exception', $ex); | |
a3d5830a | 803 | } |
be6ce11c PS |
804 | |
805 | $param = validate_param(null, PARAM_INT, true); | |
806 | $this->assertSame(null, $param); | |
807 | ||
a3d5830a PS |
808 | try { |
809 | $param = validate_param(array(), PARAM_INT); | |
810 | $this->fail('invalid_parameter_exception expected'); | |
be6ce11c PS |
811 | } catch (moodle_exception $ex) { |
812 | $this->assertInstanceOf('invalid_parameter_exception', $ex); | |
a3d5830a PS |
813 | } |
814 | try { | |
815 | $param = validate_param(new stdClass, PARAM_INT); | |
816 | $this->fail('invalid_parameter_exception expected'); | |
be6ce11c PS |
817 | } catch (moodle_exception $ex) { |
818 | $this->assertInstanceOf('invalid_parameter_exception', $ex); | |
9a97fa5a | 819 | } |
be6ce11c PS |
820 | |
821 | $param = validate_param('1.0', PARAM_FLOAT); | |
822 | $this->assertSame(1.0, $param); | |
823 | ||
824 | // Make sure valid floats do not cause exception. | |
825 | validate_param(1.0, PARAM_FLOAT); | |
826 | validate_param(10, PARAM_FLOAT); | |
827 | validate_param('0', PARAM_FLOAT); | |
828 | validate_param('119813454.545464564564546564545646556564465465456465465465645645465645645645', PARAM_FLOAT); | |
829 | validate_param('011.1', PARAM_FLOAT); | |
830 | validate_param('11', PARAM_FLOAT); | |
831 | validate_param('+.1', PARAM_FLOAT); | |
832 | validate_param('-.1', PARAM_FLOAT); | |
833 | validate_param('1e10', PARAM_FLOAT); | |
834 | validate_param('.1e+10', PARAM_FLOAT); | |
835 | validate_param('1E-1', PARAM_FLOAT); | |
836 | ||
9a97fa5a PS |
837 | try { |
838 | $param = validate_param('1,2', PARAM_FLOAT); | |
839 | $this->fail('invalid_parameter_exception expected'); | |
be6ce11c PS |
840 | } catch (moodle_exception $ex) { |
841 | $this->assertInstanceOf('invalid_parameter_exception', $ex); | |
9a97fa5a PS |
842 | } |
843 | try { | |
844 | $param = validate_param('', PARAM_FLOAT); | |
845 | $this->fail('invalid_parameter_exception expected'); | |
be6ce11c PS |
846 | } catch (moodle_exception $ex) { |
847 | $this->assertInstanceOf('invalid_parameter_exception', $ex); | |
9a97fa5a PS |
848 | } |
849 | try { | |
850 | $param = validate_param('.', PARAM_FLOAT); | |
851 | $this->fail('invalid_parameter_exception expected'); | |
be6ce11c PS |
852 | } catch (moodle_exception $ex) { |
853 | $this->assertInstanceOf('invalid_parameter_exception', $ex); | |
9a97fa5a PS |
854 | } |
855 | try { | |
856 | $param = validate_param('e10', PARAM_FLOAT); | |
857 | $this->fail('invalid_parameter_exception expected'); | |
be6ce11c PS |
858 | } catch (moodle_exception $ex) { |
859 | $this->assertInstanceOf('invalid_parameter_exception', $ex); | |
9a97fa5a PS |
860 | } |
861 | try { | |
862 | $param = validate_param('abc', PARAM_FLOAT); | |
863 | $this->fail('invalid_parameter_exception expected'); | |
be6ce11c PS |
864 | } catch (moodle_exception $ex) { |
865 | $this->assertInstanceOf('invalid_parameter_exception', $ex); | |
9a97fa5a | 866 | } |
a3d5830a PS |
867 | } |
868 | ||
be6ce11c | 869 | public function test_shorten_text_no_tags_already_short_enough() { |
4a7cc248 | 870 | // ......12345678901234567890123456. |
a3d5830a | 871 | $text = "short text already no tags"; |
be6ce11c | 872 | $this->assertSame($text, shorten_text($text)); |
4a7cc248 | 873 | } |
a3d5830a | 874 | |
be6ce11c | 875 | public function test_shorten_text_with_tags_already_short_enough() { |
4a7cc248 | 876 | // .........123456...7890....12345678.......901234567. |
a3d5830a | 877 | $text = "<p>short <b>text</b> already</p><p>with tags</p>"; |
be6ce11c | 878 | $this->assertSame($text, shorten_text($text)); |
4a7cc248 | 879 | } |
a3d5830a | 880 | |
be6ce11c | 881 | public function test_shorten_text_no_tags_needs_shortening() { |
4a7cc248 TH |
882 | // Default truncation is after 30 chars, but allowing 3 for the final '...'. |
883 | // ......12345678901234567890123456789023456789012345678901234. | |
a3d5830a | 884 | $text = "long text without any tags blah de blah blah blah what"; |
be6ce11c | 885 | $this->assertSame('long text without any tags ...', shorten_text($text)); |
4a7cc248 | 886 | } |
a3d5830a | 887 | |
be6ce11c | 888 | public function test_shorten_text_with_tags_needs_shortening() { |
4a7cc248 | 889 | // .......................................123456789012345678901234567890... |
a3d5830a PS |
890 | $text = "<div class='frog'><p><blockquote>Long text with tags that will ". |
891 | "be chopped off but <b>should be added back again</b></blockquote></p></div>"; | |
892 | $this->assertEquals("<div class='frog'><p><blockquote>Long text with " . | |
893 | "tags that ...</blockquote></p></div>", shorten_text($text)); | |
4a7cc248 | 894 | } |
a3d5830a | 895 | |
4ebdd154 | 896 | public function test_shorten_text_with_tags_and_html_comment() { |
897 | $text = "<div class='frog'><p><blockquote><!--[if !IE]><!-->Long text with ". | |
898 | "tags that will<!--<![endif]--> ". | |
899 | "be chopped off but <b>should be added back again</b></blockquote></p></div>"; | |
900 | $this->assertEquals("<div class='frog'><p><blockquote><!--[if !IE]><!-->Long text with " . | |
901 | "tags that ...<!--<![endif]--></blockquote></p></div>", shorten_text($text)); | |
902 | } | |
903 | ||
be6ce11c | 904 | public function test_shorten_text_with_entities() { |
4a7cc248 TH |
905 | // Remember to allow 3 chars for the final '...'. |
906 | // ......123456789012345678901234567_____890... | |
a3d5830a | 907 | $text = "some text which shouldn't break there"; |
be6ce11c PS |
908 | $this->assertSame("some text which shouldn't ...", shorten_text($text, 31)); |
909 | $this->assertSame("some text which shouldn't ...", shorten_text($text, 30)); | |
910 | $this->assertSame("some text which shouldn't ...", shorten_text($text, 29)); | |
4a7cc248 | 911 | } |
a3d5830a | 912 | |
be6ce11c | 913 | public function test_shorten_text_known_tricky_case() { |
a3d5830a | 914 | // This case caused a bug up to 1.9.5 |
4a7cc248 | 915 | // ..........123456789012345678901234567890123456789.....0_____1___2___... |
a3d5830a | 916 | $text = "<h3>standard 'break-out' sub groups in TGs?</h3> <<There are several"; |
be6ce11c | 917 | $this->assertSame("<h3>standard 'break-out' sub groups in ...</h3>", |
4a7cc248 | 918 | shorten_text($text, 41)); |
be6ce11c | 919 | $this->assertSame("<h3>standard 'break-out' sub groups in TGs?...</h3>", |
4a7cc248 | 920 | shorten_text($text, 42)); |
be6ce11c | 921 | $this->assertSame("<h3>standard 'break-out' sub groups in TGs?</h3> ...", |
a3d5830a | 922 | shorten_text($text, 43)); |
4a7cc248 | 923 | } |
a3d5830a | 924 | |
be6ce11c | 925 | public function test_shorten_text_no_spaces() { |
4a7cc248 TH |
926 | // ..........123456789. |
927 | $text = "<h1>123456789</h1>"; // A string with no convenient breaks. | |
be6ce11c | 928 | $this->assertSame("<h1>12345...</h1>", shorten_text($text, 8)); |
4a7cc248 | 929 | } |
a3d5830a | 930 | |
be6ce11c | 931 | public function test_shorten_text_utf8_european() { |
4a7cc248 TH |
932 | // Text without tags. |
933 | // ......123456789012345678901234567. | |
a3d5830a | 934 | $text = "Žluťoučký koníček přeskočil"; |
be6ce11c PS |
935 | $this->assertSame($text, shorten_text($text)); // 30 chars by default. |
936 | $this->assertSame("Žluťoučký koníče...", shorten_text($text, 19, true)); | |
937 | $this->assertSame("Žluťoučký ...", shorten_text($text, 19, false)); | |
4a7cc248 | 938 | // And try it with 2-less (that are, in bytes, the middle of a sequence). |
be6ce11c PS |
939 | $this->assertSame("Žluťoučký koní...", shorten_text($text, 17, true)); |
940 | $this->assertSame("Žluťoučký ...", shorten_text($text, 17, false)); | |
a3d5830a | 941 | |
4a7cc248 | 942 | // .........123456789012345678...901234567....89012345. |
a3d5830a | 943 | $text = "<p>Žluťoučký koníček <b>přeskočil</b> potůček</p>"; |
be6ce11c PS |
944 | $this->assertSame($text, shorten_text($text, 60)); |
945 | $this->assertSame("<p>Žluťoučký koníček ...</p>", shorten_text($text, 21)); | |
946 | $this->assertSame("<p>Žluťoučký koníče...</p>", shorten_text($text, 19, true)); | |
947 | $this->assertSame("<p>Žluťoučký ...</p>", shorten_text($text, 19, false)); | |
4a7cc248 | 948 | // And try it with 2 fewer (that are, in bytes, the middle of a sequence). |
be6ce11c PS |
949 | $this->assertSame("<p>Žluťoučký koní...</p>", shorten_text($text, 17, true)); |
950 | $this->assertSame("<p>Žluťoučký ...</p>", shorten_text($text, 17, false)); | |
4a7cc248 | 951 | // And try over one tag (start/end), it does proper text len. |
be6ce11c PS |
952 | $this->assertSame("<p>Žluťoučký koníček <b>př...</b></p>", shorten_text($text, 23, true)); |
953 | $this->assertSame("<p>Žluťoučký koníček <b>přeskočil</b> pot...</p>", shorten_text($text, 34, true)); | |
4a7cc248 | 954 | // And in the middle of one tag. |
be6ce11c | 955 | $this->assertSame("<p>Žluťoučký koníček <b>přeskočil...</b></p>", shorten_text($text, 30, true)); |
4a7cc248 | 956 | } |
a3d5830a | 957 | |
be6ce11c | 958 | public function test_shorten_text_utf8_oriental() { |
a3d5830a | 959 | // Japanese |
4a7cc248 TH |
960 | // text without tags |
961 | // ......123456789012345678901234. | |
a3d5830a | 962 | $text = '言語設定言語設定abcdefghijkl'; |
be6ce11c PS |
963 | $this->assertSame($text, shorten_text($text)); // 30 chars by default. |
964 | $this->assertSame("言語設定言語...", shorten_text($text, 9, true)); | |
965 | $this->assertSame("言語設定言語...", shorten_text($text, 9, false)); | |
966 | $this->assertSame("言語設定言語設定ab...", shorten_text($text, 13, true)); | |
967 | $this->assertSame("言語設定言語設定...", shorten_text($text, 13, false)); | |
a3d5830a PS |
968 | |
969 | // Chinese | |
4a7cc248 TH |
970 | // text without tags |
971 | // ......123456789012345678901234. | |
a3d5830a | 972 | $text = '简体中文简体中文abcdefghijkl'; |
be6ce11c PS |
973 | $this->assertSame($text, shorten_text($text)); // 30 chars by default. |
974 | $this->assertSame("简体中文简体...", shorten_text($text, 9, true)); | |
975 | $this->assertSame("简体中文简体...", shorten_text($text, 9, false)); | |
976 | $this->assertSame("简体中文简体中文ab...", shorten_text($text, 13, true)); | |
977 | $this->assertSame("简体中文简体中文...", shorten_text($text, 13, false)); | |
4a7cc248 | 978 | } |
a3d5830a | 979 | |
be6ce11c | 980 | public function test_shorten_text_multilang() { |
4a7cc248 TH |
981 | // This is not necessaryily specific to multilang. The issue is really |
982 | // tags with attributes, where before we were generating invalid HTML | |
be6ce11c | 983 | // output like shorten_text('<span id="x" class="y">A</span> B', 1) |
4a7cc248 TH |
984 | // returning '<span id="x" ...</span>'. It is just that multilang |
985 | // requires the sort of HTML that is quite likely to trigger this. | |
986 | // ........................................1... | |
987 | $text = '<span lang="en" class="multilang">A</span>' . | |
988 | '<span lang="fr" class="multilang">B</span>'; | |
be6ce11c | 989 | $this->assertSame('<span lang="en" class="multilang">...</span>', |
4a7cc248 | 990 | shorten_text($text, 1)); |
a3d5830a PS |
991 | } |
992 | ||
be6ce11c | 993 | public function test_usergetdate() { |
a3d5830a | 994 | global $USER, $CFG, $DB; |
be6ce11c | 995 | $this->resetAfterTest(); |
a3d5830a | 996 | |
be6ce11c | 997 | $this->setAdminUser(); |
a3d5830a | 998 | |
be6ce11c | 999 | $USER->timezone = 2;// Set the timezone to a known state. |
a3d5830a | 1000 | |
be6ce11c | 1001 | $ts = 1261540267; // The time this function was created. |
a3d5830a | 1002 | |
be6ce11c | 1003 | $arr = usergetdate($ts, 1); // Specify the timezone as an argument. |
a3d5830a PS |
1004 | $arr = array_values($arr); |
1005 | ||
be6ce11c PS |
1006 | list($seconds, $minutes, $hours, $mday, $wday, $mon, $year, $yday, $weekday, $month) = $arr; |
1007 | $this->assertSame(7, $seconds); | |
1008 | $this->assertSame(51, $minutes); | |
1009 | $this->assertSame(4, $hours); | |
1010 | $this->assertSame(23, $mday); | |
1011 | $this->assertSame(3, $wday); | |
1012 | $this->assertSame(12, $mon); | |
1013 | $this->assertSame(2009, $year); | |
1014 | $this->assertSame(356, $yday); | |
1015 | $this->assertSame('Wednesday', $weekday); | |
1016 | $this->assertSame('December', $month); | |
1017 | $arr = usergetdate($ts); // Gets the timezone from the $USER object. | |
a3d5830a PS |
1018 | $arr = array_values($arr); |
1019 | ||
be6ce11c PS |
1020 | list($seconds, $minutes, $hours, $mday, $wday, $mon, $year, $yday, $weekday, $month) = $arr; |
1021 | $this->assertSame(7, $seconds); | |
1022 | $this->assertSame(51, $minutes); | |
1023 | $this->assertSame(5, $hours); | |
1024 | $this->assertSame(23, $mday); | |
1025 | $this->assertSame(3, $wday); | |
1026 | $this->assertSame(12, $mon); | |
1027 | $this->assertSame(2009, $year); | |
1028 | $this->assertSame(356, $yday); | |
1029 | $this->assertSame('Wednesday', $weekday); | |
1030 | $this->assertSame('December', $month); | |
a3d5830a PS |
1031 | } |
1032 | ||
1033 | public function test_mark_user_preferences_changed() { | |
be6ce11c PS |
1034 | $this->resetAfterTest(); |
1035 | $otheruser = $this->getDataGenerator()->create_user(); | |
1036 | $otheruserid = $otheruser->id; | |
a3d5830a | 1037 | |
be6ce11c | 1038 | set_cache_flag('userpreferenceschanged', $otheruserid, null); |
a3d5830a PS |
1039 | mark_user_preferences_changed($otheruserid); |
1040 | ||
1041 | $this->assertEquals(get_cache_flag('userpreferenceschanged', $otheruserid, time()-10), 1); | |
be6ce11c | 1042 | set_cache_flag('userpreferenceschanged', $otheruserid, null); |
a3d5830a PS |
1043 | } |
1044 | ||
1045 | public function test_check_user_preferences_loaded() { | |
1046 | global $DB; | |
be6ce11c | 1047 | $this->resetAfterTest(); |
a3d5830a | 1048 | |
be6ce11c PS |
1049 | $otheruser = $this->getDataGenerator()->create_user(); |
1050 | $otheruserid = $otheruser->id; | |
a3d5830a PS |
1051 | |
1052 | $DB->delete_records('user_preferences', array('userid'=>$otheruserid)); | |
be6ce11c | 1053 | set_cache_flag('userpreferenceschanged', $otheruserid, null); |
a3d5830a PS |
1054 | |
1055 | $user = new stdClass(); | |
1056 | $user->id = $otheruserid; | |
1057 | ||
be6ce11c | 1058 | // Load. |
a3d5830a PS |
1059 | check_user_preferences_loaded($user); |
1060 | $this->assertTrue(isset($user->preference)); | |
1061 | $this->assertTrue(is_array($user->preference)); | |
be6ce11c PS |
1062 | $this->assertArrayHasKey('_lastloaded', $user->preference); |
1063 | $this->assertCount(1, $user->preference); | |
a3d5830a | 1064 | |
be6ce11c | 1065 | // Add preference via direct call. |
a3d5830a PS |
1066 | $DB->insert_record('user_preferences', array('name'=>'xxx', 'value'=>'yyy', 'userid'=>$user->id)); |
1067 | ||
be6ce11c | 1068 | // No cache reload yet. |
a3d5830a | 1069 | check_user_preferences_loaded($user); |
be6ce11c | 1070 | $this->assertCount(1, $user->preference); |
a3d5830a | 1071 | |
be6ce11c | 1072 | // Forced reloading of cache. |
a3d5830a PS |
1073 | unset($user->preference); |
1074 | check_user_preferences_loaded($user); | |
be6ce11c PS |
1075 | $this->assertCount(2, $user->preference); |
1076 | $this->assertSame('yyy', $user->preference['xxx']); | |
a3d5830a | 1077 | |
be6ce11c | 1078 | // Add preference via direct call. |
a3d5830a PS |
1079 | $DB->insert_record('user_preferences', array('name'=>'aaa', 'value'=>'bbb', 'userid'=>$user->id)); |
1080 | ||
be6ce11c | 1081 | // Test timeouts and modifications from different session. |
a3d5830a PS |
1082 | set_cache_flag('userpreferenceschanged', $user->id, 1, time() + 1000); |
1083 | $user->preference['_lastloaded'] = $user->preference['_lastloaded'] - 20; | |
1084 | check_user_preferences_loaded($user); | |
be6ce11c | 1085 | $this->assertCount(2, $user->preference); |
a3d5830a | 1086 | check_user_preferences_loaded($user, 10); |
be6ce11c PS |
1087 | $this->assertCount(3, $user->preference); |
1088 | $this->assertSame('bbb', $user->preference['aaa']); | |
a3d5830a PS |
1089 | set_cache_flag('userpreferenceschanged', $user->id, null); |
1090 | } | |
1091 | ||
1092 | public function test_set_user_preference() { | |
1093 | global $DB, $USER; | |
be6ce11c | 1094 | $this->resetAfterTest(); |
a3d5830a | 1095 | |
be6ce11c | 1096 | $this->setAdminUser(); |
a3d5830a | 1097 | |
be6ce11c PS |
1098 | $otheruser = $this->getDataGenerator()->create_user(); |
1099 | $otheruserid = $otheruser->id; | |
a3d5830a PS |
1100 | |
1101 | $DB->delete_records('user_preferences', array('userid'=>$otheruserid)); | |
1102 | set_cache_flag('userpreferenceschanged', $otheruserid, null); | |
1103 | ||
1104 | $user = new stdClass(); | |
1105 | $user->id = $otheruserid; | |
1106 | ||
1107 | set_user_preference('aaa', 'bbb', $otheruserid); | |
be6ce11c PS |
1108 | $this->assertSame('bbb', $DB->get_field('user_preferences', 'value', array('userid'=>$otheruserid, 'name'=>'aaa'))); |
1109 | $this->assertSame('bbb', get_user_preferences('aaa', null, $otheruserid)); | |
a3d5830a PS |
1110 | |
1111 | set_user_preference('xxx', 'yyy', $user); | |
be6ce11c PS |
1112 | $this->assertSame('yyy', $DB->get_field('user_preferences', 'value', array('userid'=>$otheruserid, 'name'=>'xxx'))); |
1113 | $this->assertSame('yyy', get_user_preferences('xxx', null, $otheruserid)); | |
a3d5830a | 1114 | $this->assertTrue(is_array($user->preference)); |
be6ce11c PS |
1115 | $this->assertSame('bbb', $user->preference['aaa']); |
1116 | $this->assertSame('yyy', $user->preference['xxx']); | |
a3d5830a | 1117 | |
be6ce11c PS |
1118 | set_user_preference('xxx', null, $user); |
1119 | $this->assertFalse($DB->get_field('user_preferences', 'value', array('userid'=>$otheruserid, 'name'=>'xxx'))); | |
1120 | $this->assertNull(get_user_preferences('xxx', null, $otheruserid)); | |
a3d5830a PS |
1121 | |
1122 | set_user_preference('ooo', true, $user); | |
1123 | $prefs = get_user_preferences(null, null, $otheruserid); | |
be6ce11c PS |
1124 | $this->assertSame($user->preference['aaa'], $prefs['aaa']); |
1125 | $this->assertSame($user->preference['ooo'], $prefs['ooo']); | |
1126 | $this->assertSame('1', $prefs['ooo']); | |
a3d5830a PS |
1127 | |
1128 | set_user_preference('null', 0, $user); | |
1129 | $this->assertSame('0', get_user_preferences('null', null, $otheruserid)); | |
1130 | ||
1131 | $this->assertSame('lala', get_user_preferences('undefined', 'lala', $otheruserid)); | |
1132 | ||
1133 | $DB->delete_records('user_preferences', array('userid'=>$otheruserid)); | |
1134 | set_cache_flag('userpreferenceschanged', $otheruserid, null); | |
1135 | ||
be6ce11c | 1136 | // Test $USER default. |
a3d5830a PS |
1137 | set_user_preference('_test_user_preferences_pref', 'ok'); |
1138 | $this->assertSame('ok', $USER->preference['_test_user_preferences_pref']); | |
1139 | unset_user_preference('_test_user_preferences_pref'); | |
1140 | $this->assertTrue(!isset($USER->preference['_test_user_preferences_pref'])); | |
1141 | ||
be6ce11c | 1142 | // Test 1333 char values (no need for unicode, there are already tests for that in DB tests). |
a3d5830a PS |
1143 | $longvalue = str_repeat('a', 1333); |
1144 | set_user_preference('_test_long_user_preference', $longvalue); | |
1145 | $this->assertEquals($longvalue, get_user_preferences('_test_long_user_preference')); | |
1146 | $this->assertEquals($longvalue, | |
1147 | $DB->get_field('user_preferences', 'value', array('userid' => $USER->id, 'name' => '_test_long_user_preference'))); | |
1148 | ||
be6ce11c | 1149 | // Test > 1333 char values, coding_exception expected. |
a3d5830a PS |
1150 | $longvalue = str_repeat('a', 1334); |
1151 | try { | |
1152 | set_user_preference('_test_long_user_preference', $longvalue); | |
17222a4a | 1153 | $this->fail('Exception expected - longer than 1333 chars not allowed as preference value'); |
be6ce11c PS |
1154 | } catch (moodle_exception $ex) { |
1155 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
1156 | } |
1157 | ||
be6ce11c | 1158 | // Test invalid params. |
a3d5830a PS |
1159 | try { |
1160 | set_user_preference('_test_user_preferences_pref', array()); | |
17222a4a | 1161 | $this->fail('Exception expected - array not valid preference value'); |
be6ce11c PS |
1162 | } catch (moodle_exception $ex) { |
1163 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
1164 | } |
1165 | try { | |
1166 | set_user_preference('_test_user_preferences_pref', new stdClass); | |
17222a4a | 1167 | $this->fail('Exception expected - class not valid preference value'); |
be6ce11c PS |
1168 | } catch (moodle_exception $ex) { |
1169 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
1170 | } |
1171 | try { | |
17222a4a MN |
1172 | set_user_preference('_test_user_preferences_pref', 1, array('xx' => 1)); |
1173 | $this->fail('Exception expected - user instance expected'); | |
be6ce11c PS |
1174 | } catch (moodle_exception $ex) { |
1175 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
1176 | } |
1177 | try { | |
1178 | set_user_preference('_test_user_preferences_pref', 1, 'abc'); | |
17222a4a | 1179 | $this->fail('Exception expected - user instance expected'); |
be6ce11c PS |
1180 | } catch (moodle_exception $ex) { |
1181 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
1182 | } |
1183 | try { | |
1184 | set_user_preference('', 1); | |
17222a4a | 1185 | $this->fail('Exception expected - invalid name accepted'); |
be6ce11c PS |
1186 | } catch (moodle_exception $ex) { |
1187 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a PS |
1188 | } |
1189 | try { | |
1190 | set_user_preference('1', 1); | |
17222a4a | 1191 | $this->fail('Exception expected - invalid name accepted'); |
be6ce11c PS |
1192 | } catch (moodle_exception $ex) { |
1193 | $this->assertInstanceOf('coding_exception', $ex); | |
a3d5830a | 1194 | } |
a3d5830a PS |
1195 | } |
1196 | ||
2524990d TH |
1197 | public function test_set_user_preference_for_current_user() { |
1198 | global $USER; | |
1199 | $this->resetAfterTest(); | |
1200 | $this->setAdminUser(); | |
1201 | ||
1202 | set_user_preference('test_pref', 2); | |
1203 | set_user_preference('test_pref', 1, $USER->id); | |
1204 | $this->assertEquals(1, get_user_preferences('test_pref')); | |
1205 | } | |
1206 | ||
1207 | public function test_unset_user_preference_for_current_user() { | |
1208 | global $USER; | |
1209 | $this->resetAfterTest(); | |
1210 | $this->setAdminUser(); | |
1211 | ||
1212 | set_user_preference('test_pref', 1); | |
1213 | unset_user_preference('test_pref', $USER->id); | |
1214 | $this->assertNull(get_user_preferences('test_pref')); | |
1215 | } | |
1216 | ||
a3d5830a PS |
1217 | public function test_get_extra_user_fields() { |
1218 | global $CFG, $USER, $DB; | |
be6ce11c | 1219 | $this->resetAfterTest(); |
a3d5830a | 1220 | |
be6ce11c | 1221 | $this->setAdminUser(); |
a3d5830a PS |
1222 | |
1223 | // It would be really nice if there were a way to 'mock' has_capability | |
1224 | // checks (either to return true or false) but as there is not, this | |
1225 | // test doesn't test the capability check. Presumably, anyone running | |
1226 | // unit tests will have the capability. | |
1227 | $context = context_system::instance(); | |
1228 | ||
be6ce11c | 1229 | // No fields. |
a3d5830a PS |
1230 | $CFG->showuseridentity = ''; |
1231 | $this->assertEquals(array(), get_extra_user_fields($context)); | |
1232 | ||
be6ce11c | 1233 | // One field. |
a3d5830a PS |
1234 | $CFG->showuseridentity = 'frog'; |
1235 | $this->assertEquals(array('frog'), get_extra_user_fields($context)); | |
1236 | ||
be6ce11c | 1237 | // Two fields. |
a3d5830a PS |
1238 | $CFG->showuseridentity = 'frog,zombie'; |
1239 | $this->assertEquals(array('frog', 'zombie'), get_extra_user_fields($context)); | |
1240 | ||
be6ce11c | 1241 | // No fields, except. |
a3d5830a PS |
1242 | $CFG->showuseridentity = ''; |
1243 | $this->assertEquals(array(), get_extra_user_fields($context, array('frog'))); | |
1244 | ||
be6ce11c | 1245 | // One field. |
a3d5830a PS |
1246 | $CFG->showuseridentity = 'frog'; |
1247 | $this->assertEquals(array(), get_extra_user_fields($context, array('frog'))); | |
1248 | ||
be6ce11c | 1249 | // Two fields. |
a3d5830a PS |
1250 | $CFG->showuseridentity = 'frog,zombie'; |
1251 | $this->assertEquals(array('zombie'), get_extra_user_fields($context, array('frog'))); | |
a3d5830a PS |
1252 | } |
1253 | ||
1254 | public function test_get_extra_user_fields_sql() { | |
1255 | global $CFG, $USER, $DB; | |
be6ce11c | 1256 | $this->resetAfterTest(); |
a3d5830a | 1257 | |
be6ce11c | 1258 | $this->setAdminUser(); |
a3d5830a | 1259 | |
a3d5830a PS |
1260 | $context = context_system::instance(); |
1261 | ||
be6ce11c | 1262 | // No fields. |
a3d5830a | 1263 | $CFG->showuseridentity = ''; |
be6ce11c | 1264 | $this->assertSame('', get_extra_user_fields_sql($context)); |
a3d5830a | 1265 | |
be6ce11c | 1266 | // One field. |
a3d5830a | 1267 | $CFG->showuseridentity = 'frog'; |
be6ce11c | 1268 | $this->assertSame(', frog', get_extra_user_fields_sql($context)); |
a3d5830a | 1269 | |
be6ce11c | 1270 | // Two fields with table prefix. |
a3d5830a | 1271 | $CFG->showuseridentity = 'frog,zombie'; |
be6ce11c | 1272 | $this->assertSame(', u1.frog, u1.zombie', get_extra_user_fields_sql($context, 'u1')); |
a3d5830a | 1273 | |
be6ce11c | 1274 | // Two fields with field prefix. |
a3d5830a | 1275 | $CFG->showuseridentity = 'frog,zombie'; |
be6ce11c | 1276 | $this->assertSame(', frog AS u_frog, zombie AS u_zombie', |
a3d5830a PS |
1277 | get_extra_user_fields_sql($context, '', 'u_')); |
1278 | ||
be6ce11c | 1279 | // One field excluded. |
a3d5830a | 1280 | $CFG->showuseridentity = 'frog'; |
be6ce11c | 1281 | $this->assertSame('', get_extra_user_fields_sql($context, '', '', array('frog'))); |
a3d5830a | 1282 | |
be6ce11c | 1283 | // Two fields, one excluded, table+field prefix. |
a3d5830a PS |
1284 | $CFG->showuseridentity = 'frog,zombie'; |
1285 | $this->assertEquals(', u1.zombie AS u_zombie', | |
1286 | get_extra_user_fields_sql($context, 'u1', 'u_', array('frog'))); | |
a3d5830a PS |
1287 | } |
1288 | ||
d506f422 EL |
1289 | /** |
1290 | * Test some critical TZ/DST. | |
1291 | * | |
1292 | * This method tests some special TZ/DST combinations that were fixed | |
1293 | * by MDL-38999. The tests are done by comparing the results of the | |
1294 | * output using Moodle TZ/DST support and PHP native one. | |
1295 | * | |
1296 | * Note: If you don't trust PHP TZ/DST support, can verify the | |
1297 | * harcoded expectations below with: | |
1298 | * http://www.tools4noobs.com/online_tools/unix_timestamp_to_datetime/ | |
1299 | */ | |
1300 | public function test_some_moodle_special_dst() { | |
1301 | $stamp = 1365386400; // 2013/04/08 02:00:00 GMT/UTC. | |
1302 | ||
1303 | // In Europe/Tallinn it was 2013/04/08 05:00:00. | |
1304 | $expectation = '2013/04/08 05:00:00'; | |
1305 | $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC')); | |
1306 | $phpdt->setTimezone(new DateTimeZone('Europe/Tallinn')); | |
1307 | $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result. | |
1308 | $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'Europe/Tallinn', false); // Moodle result. | |
1309 | $this->assertSame($expectation, $phpres); | |
1310 | $this->assertSame($expectation, $moodleres); | |
1311 | ||
1312 | // In St. Johns it was 2013/04/07 23:30:00. | |
1313 | $expectation = '2013/04/07 23:30:00'; | |
1314 | $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC')); | |
1315 | $phpdt->setTimezone(new DateTimeZone('America/St_Johns')); | |
1316 | $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result. | |
1317 | $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'America/St_Johns', false); // Moodle result. | |
1318 | $this->assertSame($expectation, $phpres); | |
1319 | $this->assertSame($expectation, $moodleres); | |
1320 | ||
1321 | $stamp = 1383876000; // 2013/11/08 02:00:00 GMT/UTC. | |
1322 | ||
1323 | // In Europe/Tallinn it was 2013/11/08 04:00:00. | |
1324 | $expectation = '2013/11/08 04:00:00'; | |
1325 | $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC')); | |
1326 | $phpdt->setTimezone(new DateTimeZone('Europe/Tallinn')); | |
1327 | $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result. | |
1328 | $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'Europe/Tallinn', false); // Moodle result. | |
1329 | $this->assertSame($expectation, $phpres); | |
1330 | $this->assertSame($expectation, $moodleres); | |
1331 | ||
1332 | // In St. Johns it was 2013/11/07 22:30:00. | |
1333 | $expectation = '2013/11/07 22:30:00'; | |
1334 | $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC')); | |
1335 | $phpdt->setTimezone(new DateTimeZone('America/St_Johns')); | |
1336 | $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result. | |
1337 | $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'America/St_Johns', false); // Moodle result. | |
1338 | $this->assertSame($expectation, $phpres); | |
1339 | $this->assertSame($expectation, $moodleres); | |
1340 | } | |
1341 | ||
a3d5830a PS |
1342 | public function test_userdate() { |
1343 | global $USER, $CFG, $DB; | |
be6ce11c | 1344 | $this->resetAfterTest(); |
a3d5830a | 1345 | |
be6ce11c | 1346 | $this->setAdminUser(); |
a3d5830a PS |
1347 | |
1348 | $testvalues = array( | |
1349 | array( | |
1350 | 'time' => '1309514400', | |
1351 | 'usertimezone' => 'America/Moncton', | |
be6ce11c | 1352 | 'timezone' => '0.0', // No dst offset. |
a3d5830a PS |
1353 | 'expectedoutput' => 'Friday, 1 July 2011, 10:00 AM' |
1354 | ), | |
1355 | array( | |
1356 | 'time' => '1309514400', | |
1357 | 'usertimezone' => 'America/Moncton', | |
be6ce11c | 1358 | 'timezone' => '99', // Dst offset and timezone offset. |
a3d5830a PS |
1359 | 'expectedoutput' => 'Friday, 1 July 2011, 7:00 AM' |
1360 | ), | |
1361 | array( | |
1362 | 'time' => '1309514400', | |
1363 | 'usertimezone' => 'America/Moncton', | |
be6ce11c | 1364 | 'timezone' => 'America/Moncton', // Dst offset and timezone offset. |
a3d5830a PS |
1365 | 'expectedoutput' => 'Friday, 1 July 2011, 7:00 AM' |
1366 | ), | |
1367 | array( | |
1368 | 'time' => '1293876000 ', | |
1369 | 'usertimezone' => 'America/Moncton', | |
be6ce11c | 1370 | 'timezone' => '0.0', // No dst offset. |
a3d5830a PS |
1371 | 'expectedoutput' => 'Saturday, 1 January 2011, 10:00 AM' |
1372 | ), | |
1373 | array( | |
1374 | 'time' => '1293876000 ', | |
1375 | 'usertimezone' => 'America/Moncton', | |
be6ce11c | 1376 | 'timezone' => '99', // No dst offset in jan, so just timezone offset. |
a3d5830a PS |
1377 | 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 AM' |
1378 | ), | |
1379 | array( | |
1380 | 'time' => '1293876000 ', | |
1381 | 'usertimezone' => 'America/Moncton', | |
be6ce11c | 1382 | 'timezone' => 'America/Moncton', // No dst offset in jan. |
a3d5830a PS |
1383 | 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 AM' |
1384 | ), | |
1385 | array( | |
1386 | 'time' => '1293876000 ', | |
1387 | 'usertimezone' => '2', | |
be6ce11c | 1388 | 'timezone' => '99', // Take user timezone. |
a3d5830a PS |
1389 | 'expectedoutput' => 'Saturday, 1 January 2011, 12:00 PM' |
1390 | ), | |
1391 | array( | |
1392 | 'time' => '1293876000 ', | |
1393 | 'usertimezone' => '-2', | |
be6ce11c | 1394 | 'timezone' => '99', // Take user timezone. |
a3d5830a PS |
1395 | 'expectedoutput' => 'Saturday, 1 January 2011, 8:00 AM' |
1396 | ), | |
1397 | array( | |
1398 | 'time' => '1293876000 ', | |
1399 | 'usertimezone' => '-10', | |
be6ce11c | 1400 | 'timezone' => '2', // Take this timezone. |
a3d5830a PS |
1401 | 'expectedoutput' => 'Saturday, 1 January 2011, 12:00 PM' |
1402 | ), | |
1403 | array( | |
1404 | 'time' => '1293876000 ', | |
1405 | 'usertimezone' => '-10', | |
be6ce11c | 1406 | 'timezone' => '-2', // Take this timezone. |
a3d5830a PS |
1407 | 'expectedoutput' => 'Saturday, 1 January 2011, 8:00 AM' |
1408 | ), | |
1409 | array( | |
1410 | 'time' => '1293876000 ', | |
1411 | 'usertimezone' => '-10', | |
be6ce11c | 1412 | 'timezone' => 'random/time', // This should show server time. |
a3d5830a PS |
1413 | 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 PM' |
1414 | ), | |
1415 | array( | |
1416 | 'time' => '1293876000 ', | |
d6e7a63d | 1417 | 'usertimezone' => '20', // Fallback to server time zone. |
be6ce11c | 1418 | 'timezone' => '99', // This should show user time. |
a3d5830a PS |
1419 | 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 PM' |
1420 | ), | |
1421 | ); | |
1422 | ||
be6ce11c | 1423 | // Set default timezone to Australia/Perth, else time calculated |
d6e7a63d PS |
1424 | // will not match expected values. |
1425 | $this->setTimezone(99, 'Australia/Perth'); | |
a3d5830a PS |
1426 | |
1427 | foreach ($testvalues as $vals) { | |
1428 | $USER->timezone = $vals['usertimezone']; | |
1429 | $actualoutput = userdate($vals['time'], '%A, %d %B %Y, %I:%M %p', $vals['timezone']); | |
1430 | ||
be6ce11c | 1431 | // On different systems case of AM PM changes so compare case insensitive. |
2f1e464a PS |
1432 | $vals['expectedoutput'] = core_text::strtolower($vals['expectedoutput']); |
1433 | $actualoutput = core_text::strtolower($actualoutput); | |
a3d5830a | 1434 | |
be6ce11c | 1435 | $this->assertSame($vals['expectedoutput'], $actualoutput, |
d6e7a63d | 1436 | "Expected: {$vals['expectedoutput']} => Actual: {$actualoutput} \ndata: " . var_export($vals, true)); |
a3d5830a | 1437 | } |
a3d5830a PS |
1438 | } |
1439 | ||
980a3a12 PS |
1440 | /** |
1441 | * Make sure the DST changes happen at the right time in Moodle. | |
1442 | */ | |
1443 | public function test_dst_changes() { | |
1444 | // DST switching in Prague. | |
1445 | // From 2AM to 3AM in 1989. | |
1446 | $date = new DateTime('1989-03-26T01:59:00+01:00'); | |
b6e98dc8 | 1447 | $this->assertSame('Sunday, 26 March 1989, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 | 1448 | $date = new DateTime('1989-03-26T02:01:00+01:00'); |
b6e98dc8 | 1449 | $this->assertSame('Sunday, 26 March 1989, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 PS |
1450 | // From 3AM to 2AM in 1989 - not the same as the west Europe. |
1451 | $date = new DateTime('1989-09-24T01:59:00+01:00'); | |
b6e98dc8 | 1452 | $this->assertSame('Sunday, 24 September 1989, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 | 1453 | $date = new DateTime('1989-09-24T02:01:00+01:00'); |
b6e98dc8 | 1454 | $this->assertSame('Sunday, 24 September 1989, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 PS |
1455 | // From 2AM to 3AM in 2014. |
1456 | $date = new DateTime('2014-03-30T01:59:00+01:00'); | |
b6e98dc8 | 1457 | $this->assertSame('Sunday, 30 March 2014, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 | 1458 | $date = new DateTime('2014-03-30T02:01:00+01:00'); |
b6e98dc8 | 1459 | $this->assertSame('Sunday, 30 March 2014, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 PS |
1460 | // From 3AM to 2AM in 2014. |
1461 | $date = new DateTime('2014-10-26T01:59:00+01:00'); | |
b6e98dc8 | 1462 | $this->assertSame('Sunday, 26 October 2014, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 | 1463 | $date = new DateTime('2014-10-26T02:01:00+01:00'); |
b6e98dc8 | 1464 | $this->assertSame('Sunday, 26 October 2014, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 PS |
1465 | // From 2AM to 3AM in 2020. |
1466 | $date = new DateTime('2020-03-29T01:59:00+01:00'); | |
b6e98dc8 | 1467 | $this->assertSame('Sunday, 29 March 2020, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 | 1468 | $date = new DateTime('2020-03-29T02:01:00+01:00'); |
b6e98dc8 | 1469 | $this->assertSame('Sunday, 29 March 2020, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 PS |
1470 | // From 3AM to 2AM in 2020. |
1471 | $date = new DateTime('2020-10-25T01:59:00+01:00'); | |
b6e98dc8 | 1472 | $this->assertSame('Sunday, 25 October 2020, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 | 1473 | $date = new DateTime('2020-10-25T02:01:00+01:00'); |
b6e98dc8 | 1474 | $this->assertSame('Sunday, 25 October 2020, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Europe/Prague')); |
980a3a12 PS |
1475 | |
1476 | // DST switching in NZ. | |
1477 | // From 3AM to 2AM in 2015. | |
1478 | $date = new DateTime('2015-04-05T02:59:00+13:00'); | |
b6e98dc8 | 1479 | $this->assertSame('Sunday, 5 April 2015, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Pacific/Auckland')); |
980a3a12 | 1480 | $date = new DateTime('2015-04-05T03:01:00+13:00'); |
b6e98dc8 | 1481 | $this->assertSame('Sunday, 5 April 2015, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Pacific/Auckland')); |
980a3a12 PS |
1482 | // From 2AM to 3AM in 2009. |
1483 | $date = new DateTime('2015-09-27T01:59:00+12:00'); | |
b6e98dc8 | 1484 | $this->assertSame('Sunday, 27 September 2015, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Pacific/Auckland')); |
980a3a12 | 1485 | $date = new DateTime('2015-09-27T02:01:00+12:00'); |
b6e98dc8 | 1486 | $this->assertSame('Sunday, 27 September 2015, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Pacific/Auckland')); |
980a3a12 PS |
1487 | |
1488 | // DST switching in Perth. | |
1489 | // From 3AM to 2AM in 2009. | |
1490 | $date = new DateTime('2008-03-30T01:59:00+08:00'); | |
b6e98dc8 | 1491 | $this->assertSame('Sunday, 30 March 2008, 02:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Australia/Perth')); |
980a3a12 | 1492 | $date = new DateTime('2008-03-30T02:01:00+08:00'); |
b6e98dc8 | 1493 | $this->assertSame('Sunday, 30 March 2008, 02:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Australia/Perth')); |
980a3a12 PS |
1494 | // From 2AM to 3AM in 2009. |
1495 | $date = new DateTime('2008-10-26T01:59:00+08:00'); | |
b6e98dc8 | 1496 | $this->assertSame('Sunday, 26 October 2008, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Australia/Perth')); |
980a3a12 | 1497 | $date = new DateTime('2008-10-26T02:01:00+08:00'); |
b6e98dc8 | 1498 | $this->assertSame('Sunday, 26 October 2008, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'Australia/Perth')); |
980a3a12 PS |
1499 | |
1500 | // DST switching in US. | |
1501 | // From 2AM to 3AM in 2014. | |
1502 | $date = new DateTime('2014-03-09T01:59:00-05:00'); | |
b6e98dc8 | 1503 | $this->assertSame('Sunday, 9 March 2014, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'America/New_York')); |
980a3a12 | 1504 | $date = new DateTime('2014-03-09T02:01:00-05:00'); |
b6e98dc8 | 1505 | $this->assertSame('Sunday, 9 March 2014, 03:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'America/New_York')); |
980a3a12 PS |
1506 | // From 3AM to 2AM in 2014. |
1507 | $date = new DateTime('2014-11-02T01:59:00-04:00'); | |
b6e98dc8 | 1508 | $this->assertSame('Sunday, 2 November 2014, 01:59', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'America/New_York')); |
980a3a12 | 1509 | $date = new DateTime('2014-11-02T02:01:00-04:00'); |
b6e98dc8 | 1510 | $this->assertSame('Sunday, 2 November 2014, 01:01', userdate($date->getTimestamp(), '%A, %d %B %Y, %H:%M', 'America/New_York')); |
980a3a12 PS |
1511 | } |
1512 | ||
a3d5830a PS |
1513 | public function test_make_timestamp() { |
1514 | global $USER, $CFG, $DB; | |
be6ce11c | 1515 | $this->resetAfterTest(); |
a3d5830a | 1516 | |
be6ce11c | 1517 | $this->setAdminUser(); |
a3d5830a PS |
1518 | |
1519 | $testvalues = array( | |
1520 | array( | |
1521 | 'usertimezone' => 'America/Moncton', | |
1522 | 'year' => '2011', | |
1523 | 'month' => '7', | |
1524 | 'day' => '1', | |
1525 | 'hour' => '10', | |
1526 | 'minutes' => '00', | |
1527 | 'seconds' => '00', | |
7b05e146 | 1528 | 'timezone' => '0.0', |
be6ce11c PS |
1529 | 'applydst' => false, // No dst offset. |
1530 | 'expectedoutput' => '1309514400' // 6pm at UTC+0. | |
a3d5830a PS |
1531 | ), |
1532 | array( | |
1533 | 'usertimezone' => 'America/Moncton', | |
1534 | 'year' => '2011', | |
1535 | 'month' => '7', | |
1536 | 'day' => '1', | |
1537 | 'hour' => '10', | |
1538 | 'minutes' => '00', | |
1539 | 'seconds' => '00', | |
be6ce11c PS |
1540 | 'timezone' => '99', // User default timezone. |
1541 | 'applydst' => false, // Don't apply dst. | |
a3d5830a PS |
1542 | 'expectedoutput' => '1309528800' |
1543 | ), | |
1544 | array( | |
1545 | 'usertimezone' => 'America/Moncton', | |
1546 | 'year' => '2011', | |
1547 | 'month' => '7', | |
1548 | 'day' => '1', | |
1549 | 'hour' => '10', | |
1550 | 'minutes' => '00', | |
1551 | 'seconds' => '00', | |
be6ce11c PS |
1552 | 'timezone' => '99', // User default timezone. |
1553 | 'applydst' => true, // Apply dst. | |
a3d5830a PS |
1554 | 'expectedoutput' => '1309525200' |
1555 | ), | |
1556 | array( | |
1557 | 'usertimezone' => 'America/Moncton', | |
1558 | 'year' => '2011', | |
1559 | 'month' => '7', | |
1560 | 'day' => '1', | |
1561 | 'hour' => '10', | |
1562 | 'minutes' => '00', | |
1563 | 'seconds' => '00', | |
be6ce11c PS |
1564 | 'timezone' => 'America/Moncton', // String timezone. |
1565 | 'applydst' => true, // Apply dst. | |
a3d5830a PS |
1566 | 'expectedoutput' => '1309525200' |
1567 | ), | |
1568 | array( | |
be6ce11c | 1569 | 'usertimezone' => '2', // No dst applyed. |
a3d5830a PS |
1570 | 'year' => '2011', |
1571 | 'month' => '7', | |
1572 | 'day' => '1', | |
1573 | 'hour' => '10', | |
1574 | 'minutes' => '00', | |
1575 | 'seconds' => '00', | |
be6ce11c PS |
1576 | 'timezone' => '99', // Take user timezone. |
1577 | 'applydst' => true, // Apply dst. | |
a3d5830a PS |
1578 | 'expectedoutput' => '1309507200' |
1579 | ), | |
1580 | array( | |
be6ce11c | 1581 | 'usertimezone' => '-2', // No dst applyed. |
a3d5830a PS |
1582 | 'year' => '2011', |
1583 | 'month' => '7', | |
1584 | 'day' => '1', | |
1585 | 'hour' => '10', | |
1586 | 'minutes' => '00', | |
1587 | 'seconds' => '00', | |
be6ce11c PS |
1588 | 'timezone' => '99', // Take usertimezone. |
1589 | 'applydst' => true, // Apply dst. | |
a3d5830a PS |
1590 | 'expectedoutput' => '1309521600' |
1591 | ), | |
1592 | array( | |
be6ce11c | 1593 | 'usertimezone' => '-10', // No dst applyed. |
a3d5830a PS |
1594 | 'year' => '2011', |
1595 | 'month' => '7', | |
1596 | 'day' => '1', | |
1597 | 'hour' => '10', | |
1598 | 'minutes' => '00', | |
1599 | 'seconds' => '00', | |
be6ce11c PS |
1600 | 'timezone' => '2', // Take this timezone. |
1601 | 'applydst' => true, // Apply dst. | |
a3d5830a PS |
1602 | 'expectedoutput' => '1309507200' |
1603 | ), | |
1604 | array( | |
be6ce11c | 1605 | 'usertimezone' => '-10', // No dst applyed. |
a3d5830a PS |
1606 | 'year' => '2011', |
1607 | 'month' => '7', | |
1608 | 'day' => '1', | |
1609 | 'hour' => '10', | |
1610 | 'minutes' => '00', | |
1611 | 'seconds' => '00', | |
be6ce11c PS |
1612 | 'timezone' => '-2', // Take this timezone. |
1613 | 'applydst' => true, // Apply dst. | |
a3d5830a PS |
1614 | 'expectedoutput' => '1309521600' |
1615 | ), | |
1616 | array( | |
be6ce11c | 1617 | 'usertimezone' => '-10', // No dst applyed. |
a3d5830a PS |
1618 | 'year' => '2011', |
1619 | 'month' => '7', | |
1620 | 'day' => '1', | |
1621 | 'hour' => '10', | |
1622 | 'minutes' => '00', | |
1623 | 'seconds' => '00', | |
be6ce11c PS |
1624 | 'timezone' => 'random/time', // This should show server time. |
1625 | 'applydst' => true, // Apply dst. | |
a3d5830a PS |
1626 | 'expectedoutput' => '1309485600' |
1627 | ), | |
1628 | array( | |
88474db2 | 1629 | 'usertimezone' => '-14', // Server time. |
a3d5830a PS |
1630 | 'year' => '2011', |
1631 | 'month' => '7', | |
1632 | 'day' => '1', | |
1633 | 'hour' => '10', | |
1634 | 'minutes' => '00', | |
1635 | 'seconds' => '00', | |
be6ce11c PS |
1636 | 'timezone' => '99', // Get user time. |
1637 | 'applydst' => true, // Apply dst. | |
a3d5830a PS |
1638 | 'expectedoutput' => '1309485600' |
1639 | ) | |
1640 | ); | |
1641 | ||
be6ce11c | 1642 | // Set default timezone to Australia/Perth, else time calculated |
d6e7a63d PS |
1643 | // will not match expected values. |
1644 | $this->setTimezone(99, 'Australia/Perth'); | |
a3d5830a | 1645 | |
be6ce11c | 1646 | // Test make_timestamp with all testvals and assert if anything wrong. |
a3d5830a PS |
1647 | foreach ($testvalues as $vals) { |
1648 | $USER->timezone = $vals['usertimezone']; | |
1649 | $actualoutput = make_timestamp( | |
1650 | $vals['year'], | |
1651 | $vals['month'], | |
1652 | $vals['day'], | |
1653 | $vals['hour'], | |
1654 | $vals['minutes'], | |
1655 | $vals['seconds'], | |
1656 | $vals['timezone'], | |
1657 | $vals['applydst'] | |
1658 | ); | |
1659 | ||
be6ce11c | 1660 | // On different systems case of AM PM changes so compare case insensitive. |
2f1e464a PS |
1661 | $vals['expectedoutput'] = core_text::strtolower($vals['expectedoutput']); |
1662 | $actualoutput = core_text::strtolower($actualoutput); | |
a3d5830a | 1663 | |
be6ce11c | 1664 | $this->assertSame($vals['expectedoutput'], $actualoutput, |
a3d5830a PS |
1665 | "Expected: {$vals['expectedoutput']} => Actual: {$actualoutput}, |
1666 | Please check if timezones are updated (Site adminstration -> location -> update timezone)"); | |
1667 | } | |
a3d5830a PS |
1668 | } |
1669 | ||
1670 | /** | |
1671 | * Test get_string and most importantly the implementation of the lang_string | |
1672 | * object. | |
1673 | */ | |
1674 | public function test_get_string() { | |
1675 | global $COURSE; | |
1676 | ||
be6ce11c | 1677 | // Make sure we are using English. |
a3d5830a PS |
1678 | $originallang = $COURSE->lang; |
1679 | $COURSE->lang = 'en'; | |
1680 | ||
1681 | $yes = get_string('yes'); | |
1682 | $yesexpected = 'Yes'; | |
aed2b9ba | 1683 | $this->assertInternalType('string', $yes); |
be6ce11c | 1684 | $this->assertSame($yesexpected, $yes); |
a3d5830a PS |
1685 | |
1686 | $yes = get_string('yes', 'moodle'); | |
aed2b9ba | 1687 | $this->assertInternalType('string', $yes); |
be6ce11c | 1688 | $this->assertSame($yesexpected, $yes); |
a3d5830a PS |
1689 | |
1690 | $yes = get_string('yes', 'core'); | |
aed2b9ba | 1691 | $this->assertInternalType('string', $yes); |
be6ce11c | 1692 | $this->assertSame($yesexpected, $yes); |
a3d5830a PS |
1693 | |
1694 | $yes = get_string('yes', ''); | |
aed2b9ba | 1695 | $this->assertInternalType('string', $yes); |
be6ce11c | 1696 | $this->assertSame($yesexpected, $yes); |
a3d5830a PS |
1697 | |
1698 | $yes = get_string('yes', null); | |
aed2b9ba | 1699 | $this->assertInternalType('string', $yes); |
be6ce11c | 1700 | $this->assertSame($yesexpected, $yes); |
a3d5830a PS |
1701 | |
1702 | $yes = get_string('yes', null, 1); | |
aed2b9ba | 1703 | $this->assertInternalType('string', $yes); |
be6ce11c | 1704 | $this->assertSame($yesexpected, $yes); |
a3d5830a PS |
1705 | |
1706 | $days = 1; | |
1707 | $numdays = get_string('numdays', 'core', '1'); | |
1708 | $numdaysexpected = $days.' days'; | |
aed2b9ba | 1709 | $this->assertInternalType('string', $numdays); |
be6ce11c | 1710 | $this->assertSame($numdaysexpected, $numdays); |
a3d5830a PS |
1711 | |
1712 | $yes = get_string('yes', null, null, true); | |
aed2b9ba | 1713 | $this->assertInstanceOf('lang_string', $yes); |
be6ce11c | 1714 | $this->assertSame($yesexpected, (string)$yes); |
a3d5830a PS |
1715 | |
1716 | // Test using a lang_string object as the $a argument for a normal | |
be6ce11c | 1717 | // get_string call (returning string). |
a3d5830a PS |
1718 | $test = new lang_string('yes', null, null, true); |
1719 | $testexpected = get_string('numdays', 'core', get_string('yes')); | |
1720 | $testresult = get_string('numdays', null, $test); | |
aed2b9ba | 1721 | $this->assertInternalType('string', $testresult); |
be6ce11c | 1722 | $this->assertSame($testexpected, $testresult); |
a3d5830a PS |
1723 | |
1724 | // Test using a lang_string object as the $a argument for an object | |
be6ce11c | 1725 | // get_string call (returning lang_string). |
a3d5830a PS |
1726 | $test = new lang_string('yes', null, null, true); |
1727 | $testexpected = get_string('numdays', 'core', get_string('yes')); | |
1728 | $testresult = get_string('numdays', null, $test, true); | |
aed2b9ba | 1729 | $this->assertInstanceOf('lang_string', $testresult); |
be6ce11c | 1730 | $this->assertSame($testexpected, "$testresult"); |
a3d5830a PS |
1731 | |
1732 | // Make sure that object properties that can't be converted don't cause | |
be6ce11c PS |
1733 | // errors. |
1734 | // Level one: This is as deep as current language processing goes. | |
a3d5830a PS |
1735 | $test = new stdClass; |
1736 | $test->one = 'here'; | |
1737 | $string = get_string('yes', null, $test, true); | |
be6ce11c | 1738 | $this->assertEquals($yesexpected, $string); |
a3d5830a PS |
1739 | |
1740 | // Make sure that object properties that can't be converted don't cause | |
1741 | // errors. | |
1742 | // Level two: Language processing doesn't currently reach this deep. | |
1743 | // only immediate scalar properties are worked with. | |
1744 | $test = new stdClass; | |
1745 | $test->one = new stdClass; | |
1746 | $test->one->two = 'here'; | |
1747 | $string = get_string('yes', null, $test, true); | |
be6ce11c | 1748 | $this->assertEquals($yesexpected, $string); |
a3d5830a PS |
1749 | |
1750 | // Make sure that object properties that can't be converted don't cause | |
1751 | // errors. | |
1752 | // Level three: It should never ever go this deep, but we're making sure | |
1753 | // it doesn't cause any probs anyway. | |
1754 | $test = new stdClass; | |
1755 | $test->one = new stdClass; | |
1756 | $test->one->two = new stdClass; | |
1757 | $test->one->two->three = 'here'; | |
1758 | $string = get_string('yes', null, $test, true); | |
be6ce11c | 1759 | $this->assertEquals($yesexpected, $string); |
a3d5830a PS |
1760 | |
1761 | // Make sure that object properties that can't be converted don't cause | |
1762 | // errors and check lang_string properties. | |
be6ce11c | 1763 | // Level one: This is as deep as current language processing goes. |
a3d5830a PS |
1764 | $test = new stdClass; |
1765 | $test->one = new lang_string('yes'); | |
1766 | $string = get_string('yes', null, $test, true); | |
be6ce11c | 1767 | $this->assertEquals($yesexpected, $string); |
a3d5830a PS |
1768 | |
1769 | // Make sure that object properties that can't be converted don't cause | |
1770 | // errors and check lang_string properties. | |
1771 | // Level two: Language processing doesn't currently reach this deep. | |
1772 | // only immediate scalar properties are worked with. | |
1773 | $test = new stdClass; | |
1774 | $test->one = new stdClass; | |
1775 | $test->one->two = new lang_string('yes'); | |
1776 | $string = get_string('yes', null, $test, true); | |
be6ce11c | 1777 | $this->assertEquals($yesexpected, $string); |
a3d5830a PS |
1778 | |
1779 | // Make sure that object properties that can't be converted don't cause | |
1780 | // errors and check lang_string properties. | |
1781 | // Level three: It should never ever go this deep, but we're making sure | |
1782 | // it doesn't cause any probs anyway. | |
1783 | $test = new stdClass; | |
1784 | $test->one = new stdClass; | |
1785 | $test->one->two = new stdClass; | |
1786 | $test->one->two->three = new lang_string('yes'); | |
1787 | $string = get_string('yes', null, $test, true); | |
be6ce11c | 1788 | $this->assertEquals($yesexpected, $string); |
a3d5830a PS |
1789 | |
1790 | // Make sure that array properties that can't be converted don't cause | |
be6ce11c | 1791 | // errors. |
a3d5830a PS |
1792 | $test = array(); |
1793 | $test['one'] = new stdClass; | |
1794 | $test['one']->two = 'here'; | |
1795 | $string = get_string('yes', null, $test, true); | |
be6ce11c | 1796 | $this->assertEquals($yesexpected, $string); |
a3d5830a | 1797 | |
be6ce11c | 1798 | // Same thing but as above except using an object... this is allowed :P. |
a3d5830a PS |
1799 | $string = get_string('yes', null, null, true); |
1800 | $object = new stdClass; | |
1801 | $object->$string = 'Yes'; | |
be6ce11c PS |
1802 | $this->assertEquals($yesexpected, $string); |
1803 | $this->assertEquals($yesexpected, $object->$string); | |
a3d5830a | 1804 | |
be6ce11c | 1805 | // Reset the language. |
a3d5830a PS |
1806 | $COURSE->lang = $originallang; |
1807 | } | |
1808 | ||
1809 | /** | |
1810 | * @expectedException PHPUnit_Framework_Error_Warning | |
a3d5830a PS |
1811 | */ |
1812 | public function test_get_string_limitation() { | |
1813 | // This is one of the limitations to the lang_string class. It can't be | |
be6ce11c | 1814 | // used as a key. |
a3d5830a PS |
1815 | $array = array(get_string('yes', null, null, true) => 'yes'); |
1816 | } | |
637da99e | 1817 | |
1818 | /** | |
1819 | * Test localised float formatting. | |
1820 | */ | |
1821 | public function test_format_float() { | |
637da99e | 1822 | |
be6ce11c | 1823 | // Special case for null. |
637da99e | 1824 | $this->assertEquals('', format_float(null)); |
1825 | ||
be6ce11c | 1826 | // Default 1 decimal place. |
637da99e | 1827 | $this->assertEquals('5.4', format_float(5.43)); |
1828 | $this->assertEquals('5.0', format_float(5.001)); | |
1829 | ||
be6ce11c | 1830 | // Custom number of decimal places. |
637da99e | 1831 | $this->assertEquals('5.43000', format_float(5.43, 5)); |
1832 | ||
be6ce11c | 1833 | // Option to strip ending zeros after rounding. |
637da99e | 1834 | $this->assertEquals('5.43', format_float(5.43, 5, true, true)); |
1835 | $this->assertEquals('5', format_float(5.0001, 3, true, true)); | |
1836 | ||
d381fb77 GPL |
1837 | // Tests with a localised decimal separator. |
1838 | $this->define_local_decimal_separator(); | |
637da99e | 1839 | |
be6ce11c | 1840 | // Localisation on (default). |
637da99e | 1841 | $this->assertEquals('5X43000', format_float(5.43, 5)); |
1842 | $this->assertEquals('5X43', format_float(5.43, 5, true, true)); | |
1843 | ||
be6ce11c | 1844 | // Localisation off. |
637da99e | 1845 | $this->assertEquals('5.43000', format_float(5.43, 5, false)); |
1846 | $this->assertEquals('5.43', format_float(5.43, 5, false, true)); | |
1847 | } | |
4366ce80 | 1848 | |
d381fb77 GPL |
1849 | /** |
1850 | * Test localised float unformatting. | |
1851 | */ | |
1852 | public function test_unformat_float() { | |
1853 | ||
1854 | // Tests without the localised decimal separator. | |
1855 | ||
1856 | // Special case for null, empty or white spaces only strings. | |
1857 | $this->assertEquals(null, unformat_float(null)); | |
1858 | $this->assertEquals(null, unformat_float('')); | |
1859 | $this->assertEquals(null, unformat_float(' ')); | |
1860 | ||
1861 | // Regular use. | |
1862 | $this->assertEquals(5.4, unformat_float('5.4')); | |
1863 | $this->assertEquals(5.4, unformat_float('5.4', true)); | |
1864 | ||
1865 | // No decimal. | |
1866 | $this->assertEquals(5.0, unformat_float('5')); | |
1867 | ||
1868 | // Custom number of decimal. | |
1869 | $this->assertEquals(5.43267, unformat_float('5.43267')); | |
1870 | ||
1871 | // Empty decimal. | |
1872 | $this->assertEquals(100.0, unformat_float('100.00')); | |
1873 | ||
1874 | // With the thousand separator. | |
1875 | $this->assertEquals(1000.0, unformat_float('1 000')); | |
1876 | $this->assertEquals(1000.32, unformat_float('1 000.32')); | |
1877 | ||
1878 | // Negative number. | |
1879 | $this->assertEquals(-100.0, unformat_float('-100')); | |
1880 | ||
1881 | // Wrong value. | |
1882 | $this->assertEquals(0.0, unformat_float('Wrong value')); | |
1883 | // Wrong value in strict mode. | |
1884 | $this->assertFalse(unformat_float('Wrong value', true)); | |
1885 | ||
1886 | // Combining options. | |
1887 | $this->assertEquals(-1023.862567, unformat_float(' -1 023.862567 ')); | |
1888 | ||
1889 | // Bad decimal separator (should crop the decimal). | |
1890 | $this->assertEquals(50.0, unformat_float('50,57')); | |
1891 | // Bad decimal separator in strict mode (should return false). | |
1892 | $this->assertFalse(unformat_float('50,57', true)); | |
1893 | ||
1894 | // Tests with a localised decimal separator. | |
1895 | $this->define_local_decimal_separator(); | |
1896 | ||
1897 | // We repeat the tests above but with the current decimal separator. | |
1898 | ||
1899 | // Regular use without and with the localised separator. | |
1900 | $this->assertEquals (5.4, unformat_float('5.4')); | |
1901 | $this->assertEquals (5.4, unformat_float('5X4')); | |
1902 | ||
1903 | // Custom number of decimal. | |
1904 | $this->assertEquals (5.43267, unformat_float('5X43267')); | |
1905 | ||
1906 | // Empty decimal. | |
1907 | $this->assertEquals (100.0, unformat_float('100X00')); | |
1908 | ||
1909 | // With the thousand separator. | |
1910 | $this->assertEquals (1000.32, unformat_float('1 000X32')); | |
1911 | ||
1912 | // Bad different separator (should crop the decimal). | |
1913 | $this->assertEquals (50.0, unformat_float('50Y57')); | |
1914 | // Bad different separator in strict mode (should return false). | |
1915 | $this->assertFalse (unformat_float('50Y57', true)); | |
1916 | ||
1917 | // Combining options. | |
1918 | $this->assertEquals (-1023.862567, unformat_float(' -1 023X862567 ')); | |
1919 | // Combining options in strict mode. | |
1920 | $this->assertEquals (-1023.862567, unformat_float(' -1 023X862567 ', true)); | |
1921 | } | |
1922 | ||
4366ce80 PS |
1923 | /** |
1924 | * Test deleting of users. | |
1925 | */ | |
1926 | public function test_delete_user() { | |
1927 | global $DB, $CFG; | |
1928 | ||
1929 | $this->resetAfterTest(); | |
1930 | ||
1931 | $guest = $DB->get_record('user', array('id'=>$CFG->siteguest), '*', MUST_EXIST); | |
1932 | $admin = $DB->get_record('user', array('id'=>$CFG->siteadmins), '*', MUST_EXIST); | |
1933 | $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1))); | |
1934 | ||
1935 | $user = $this->getDataGenerator()->create_user(array('idnumber'=>'abc')); | |
1936 | $user2 = $this->getDataGenerator()->create_user(array('idnumber'=>'xyz')); | |
a180d06c FM |
1937 | $usersharedemail1 = $this->getDataGenerator()->create_user(array('email' => 'sharedemail@example.invalid')); |
1938 | $usersharedemail2 = $this->getDataGenerator()->create_user(array('email' => 'sharedemail@example.invalid')); | |
1939 | $useremptyemail1 = $this->getDataGenerator()->create_user(array('email' => '')); | |
1940 | $useremptyemail2 = $this->getDataGenerator()->create_user(array('email' => '')); | |
4366ce80 | 1941 | |
bb78e249 RT |
1942 | // Delete user and capture event. |
1943 | $sink = $this->redirectEvents(); | |
4366ce80 | 1944 | $result = delete_user($user); |
bb78e249 RT |
1945 | $events = $sink->get_events(); |
1946 | $sink->close(); | |
1947 | $event = array_pop($events); | |
1948 | ||
1949 | // Test user is deleted in DB. | |
4366ce80 PS |
1950 | $this->assertTrue($result); |
1951 | $deluser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST); | |
1952 | $this->assertEquals(1, $deluser->deleted); | |
1953 | $this->assertEquals(0, $deluser->picture); | |
1954 | $this->assertSame('', $deluser->idnumber); | |
1955 | $this->assertSame(md5($user->username), $deluser->email); | |
1956 | $this->assertRegExp('/^'.preg_quote($user->email, '/').'\.\d*$/', $deluser->username); | |
1957 | ||
1958 | $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1))); | |
1959 | ||
bb78e249 RT |
1960 | // Test Event. |
1961 | $this->assertInstanceOf('\core\event\user_deleted', $event); | |
1962 | $this->assertSame($user->id, $event->objectid); | |
1963 | $this->assertSame('user_deleted', $event->get_legacy_eventname()); | |
1964 | $this->assertEventLegacyData($user, $event); | |
1965 | $expectedlogdata = array(SITEID, 'user', 'delete', "view.php?id=$user->id", $user->firstname.' '.$user->lastname); | |
1966 | $this->assertEventLegacyLogData($expectedlogdata, $event); | |
abedc1a8 RT |
1967 | $eventdata = $event->get_data(); |
1968 | $this->assertSame($eventdata['other']['username'], $user->username); | |
1969 | $this->assertSame($eventdata['other']['email'], $user->email); | |
1970 | $this->assertSame($eventdata['other']['idnumber'], $user->idnumber); | |
1971 | $this->assertSame($eventdata['other']['picture'], $user->picture); | |
1972 | $this->assertSame($eventdata['other']['mnethostid'], $user->mnethostid); | |
1973 | $this->assertEquals($user, $event->get_record_snapshot('user', $event->objectid)); | |
623a32e5 | 1974 | $this->assertEventContextNotUsed($event); |
4366ce80 | 1975 | |
bb78e249 | 1976 | // Try invalid params. |
4366ce80 PS |
1977 | $record = new stdClass(); |
1978 | $record->grrr = 1; | |
1979 | try { | |
1980 | delete_user($record); | |
1981 | $this->fail('Expecting exception for invalid delete_user() $user parameter'); | |
be6ce11c PS |
1982 | } catch (moodle_exception $ex) { |
1983 | $this->assertInstanceOf('coding_exception', $ex); | |
4366ce80 PS |
1984 | } |
1985 | $record->id = 1; | |
1986 | try { | |
1987 | delete_user($record); | |
1988 | $this->fail('Expecting exception for invalid delete_user() $user parameter'); | |
be6ce11c PS |
1989 | } catch (moodle_exception $ex) { |
1990 | $this->assertInstanceOf('coding_exception', $ex); | |
4366ce80 PS |
1991 | } |
1992 | ||
4366ce80 PS |
1993 | $record = new stdClass(); |
1994 | $record->id = 666; | |
1995 | $record->username = 'xx'; | |
1996 | $this->assertFalse($DB->record_exists('user', array('id'=>666))); // Any non-existent id is ok. | |
1997 | $result = delete_user($record); | |
1998 | $this->assertFalse($result); | |
1999 | ||
2000 | $result = delete_user($guest); | |
2001 | $this->assertFalse($result); | |
2002 | ||
2003 | $result = delete_user($admin); | |
2004 | $this->assertFalse($result); | |
a3b6e311 | 2005 | |
a180d06c FM |
2006 | // Simultaneously deleting users with identical email addresses. |
2007 | $result1 = delete_user($usersharedemail1); | |
2008 | $result2 = delete_user($usersharedemail2); | |
2009 | ||
2010 | $usersharedemail1after = $DB->get_record('user', array('id' => $usersharedemail1->id)); | |
2011 | $usersharedemail2after = $DB->get_record('user', array('id' => $usersharedemail2->id)); | |
2012 | $this->assertTrue($result1); | |
2013 | $this->assertTrue($result2); | |
2014 | $this->assertStringStartsWith($usersharedemail1->email . '.', $usersharedemail1after->username); | |
2015 | $this->assertStringStartsWith($usersharedemail2->email . '.', $usersharedemail2after->username); | |
2016 | ||
2017 | // Simultaneously deleting users without email addresses. | |
2018 | $result1 = delete_user($useremptyemail1); | |
2019 | $result2 = delete_user($useremptyemail2); | |
2020 | ||
2021 | $useremptyemail1after = $DB->get_record('user', array('id' => $useremptyemail1->id)); | |
2022 | $useremptyemail2after = $DB->get_record('user', array('id' => $useremptyemail2->id)); | |
2023 | $this->assertTrue($result1); | |
2024 | $this->assertTrue($result2); | |
2025 | $this->assertStringStartsWith($useremptyemail1->username . '.' . $useremptyemail1->id . '@unknownemail.invalid.', | |
2026 | $useremptyemail1after->username); | |
2027 | $this->assertStringStartsWith($useremptyemail2->username . '.' . $useremptyemail2->id . '@unknownemail.invalid.', | |
2028 | $useremptyemail2after->username); | |
2029 | ||
a3b6e311 | 2030 | $this->resetDebugging(); |
4366ce80 | 2031 | } |
7b444a96 MG |
2032 | |
2033 | /** | |
2034 | * Test function convert_to_array() | |
2035 | */ | |
2036 | public function test_convert_to_array() { | |
be6ce11c | 2037 | // Check that normal classes are converted to arrays the same way as (array) would do. |
7b444a96 MG |
2038 | $obj = new stdClass(); |
2039 | $obj->prop1 = 'hello'; | |
2040 | $obj->prop2 = array('first', 'second', 13); | |
2041 | $obj->prop3 = 15; | |
2042 | $this->assertEquals(convert_to_array($obj), (array)$obj); | |
2043 | ||
be6ce11c | 2044 | // Check that context object (with iterator) is converted to array properly. |
0601e0ee | 2045 | $obj = context_system::instance(); |
7b444a96 MG |
2046 | $ar = array( |
2047 | 'id' => $obj->id, | |
2048 | 'contextlevel' => $obj->contextlevel, | |
2049 | 'instanceid' => $obj->instanceid, | |
2050 | 'path' => $obj->path, | |
2051 | 'depth' => $obj->depth | |
2052 | ); | |
2053 | $this->assertEquals(convert_to_array($obj), $ar); | |
2054 | } | |
15396bba FM |
2055 | |
2056 | /** | |
2057 | * Test the function date_format_string(). | |
2058 | */ | |
be6ce11c | 2059 | public function test_date_format_string() { |
73d20f36 PS |
2060 | global $CFG; |
2061 | ||
d6e7a63d PS |
2062 | $this->resetAfterTest(); |
2063 | $this->setTimezone(99, 'Australia/Perth'); | |
15396bba | 2064 | |
a8397303 FM |
2065 | $tests = array( |
2066 | array( | |
2067 | 'tz' => 99, | |
2068 | 'str' => '%A, %d %B %Y, %I:%M %p', | |
2069 | 'expected' => 'Saturday, 01 January 2011, 06:00 PM' | |
2070 | ), | |
2071 | array( | |
2072 | 'tz' => 0, | |
2073 | 'str' => '%A, %d %B %Y, %I:%M %p', | |
2074 | 'expected' => 'Saturday, 01 January 2011, 10:00 AM' | |
2075 | ), | |
2076 | array( | |
d6e7a63d PS |
2077 | // Note: this function expected the timestamp in weird format before, |
2078 | // since 2.9 it uses UTC. | |
2079 | 'tz' => 'Pacific/Auckland', | |
a8397303 | 2080 | 'str' => '%A, %d %B %Y, %I:%M %p', |
d6e7a63d | 2081 | 'expected' => 'Saturday, 01 January 2011, 11:00 PM' |
a8397303 | 2082 | ), |
73d20f36 PS |
2083 | // Following tests pass on Windows only because en lang pack does |
2084 | // not contain localewincharset, in real life lang pack maintainers | |
2085 | // may use only characters that are present in localewincharset | |
2086 | // in format strings! | |
a8397303 FM |
2087 | array( |
2088 | 'tz' => 99, | |
2089 | 'str' => 'Žluťoučký koníček %A', | |
2090 | 'expected' => 'Žluťoučký koníček Saturday' | |
2091 | ), | |
2092 | array( | |
2093 | 'tz' => 99, | |
2094 | 'str' => '言語設定言語 %A', | |
2095 | 'expected' => '言語設定言語 Saturday' | |
2096 | ), | |
2097 | array( | |
2098 | 'tz' => 99, | |
2099 | 'str' => '简体中文简体 %A', | |
2100 | 'expected' => '简体中文简体 Saturday' | |
2101 | ), | |
2102 | ); | |
2103 | ||
15396bba FM |
2104 | // Note: date_format_string() uses the timezone only to differenciate |
2105 | // the server time from the UTC time. It does not modify the timestamp. | |
2106 | // Hence similar results for timezones <= 13. | |
a8397303 FM |
2107 | // On different systems case of AM PM changes so compare case insensitive. |
2108 | foreach ($tests as $test) { | |
2109 | $str = date_format_string(1293876000, $test['str'], $test['tz']); | |
2f1e464a | 2110 | $this->assertSame(core_text::strtolower($test['expected']), core_text::strtolower($str)); |
a8397303 | 2111 | } |
15396bba | 2112 | } |
7f30a644 SH |
2113 | |
2114 | public function test_get_config() { | |
2115 | global $CFG; | |
2116 | ||
2117 | $this->resetAfterTest(); | |
2118 | ||
2119 | // Preparation. | |
2120 | set_config('phpunit_test_get_config_1', 'test 1'); | |
2121 | set_config('phpunit_test_get_config_2', 'test 2', 'mod_forum'); | |
2122 | if (!is_array($CFG->config_php_settings)) { | |
2123 | $CFG->config_php_settings = array(); | |
2124 | } | |
2125 | $CFG->config_php_settings['phpunit_test_get_config_3'] = 'test 3'; | |
2126 | ||
2127 | if (!is_array($CFG->forced_plugin_settings)) { | |
2128 | $CFG->forced_plugin_settings = array(); | |
2129 | } | |
2130 | if (!array_key_exists('mod_forum', $CFG->forced_plugin_settings)) { | |
2131 | $CFG->forced_plugin_settings['mod_forum'] = array(); | |
2132 | } | |
2133 | $CFG->forced_plugin_settings['mod_forum']['phpunit_test_get_config_4'] = 'test 4'; | |
2134 | $CFG->phpunit_test_get_config_5 = 'test 5'; | |
2135 | ||
2136 | // Testing. | |
be6ce11c PS |
2137 | $this->assertSame('test 1', get_config('core', 'phpunit_test_get_config_1')); |
2138 | $this->assertSame('test 2', get_config('mod_forum', 'phpunit_test_get_config_2')); | |
2139 | $this->assertSame('test 3', get_config('core', 'phpunit_test_get_config_3')); | |
2140 | $this->assertSame('test 4', get_config('mod_forum', 'phpunit_test_get_config_4')); | |
7f30a644 SH |
2141 | $this->assertFalse(get_config('core', 'phpunit_test_get_config_5')); |
2142 | $this->assertFalse(get_config('core', 'phpunit_test_get_config_x')); | |
2143 | $this->assertFalse(get_config('mod_forum', 'phpunit_test_get_config_x')); | |
2144 | ||
2145 | // Test config we know to exist. | |
be6ce11c PS |
2146 | $this->assertSame($CFG->dataroot, get_config('core', 'dataroot')); |
2147 | $this->assertSame($CFG->phpunit_dataroot, get_config('core', 'phpunit_dataroot')); | |
2148 | $this->assertSame($CFG->dataroot, get_config('core', 'phpunit_dataroot')); | |
2149 | $this->assertSame(get_config('core', 'dataroot'), get_config('core', 'phpunit_dataroot')); | |
7f30a644 SH |
2150 | |
2151 | // Test setting a config var that already exists. | |
2152 | set_config('phpunit_test_get_config_1', 'test a'); | |
be6ce11c PS |
2153 | $this->assertSame('test a', $CFG->phpunit_test_get_config_1); |
2154 | $this->assertSame('test a', get_config('core', 'phpunit_test_get_config_1')); | |
7f30a644 SH |
2155 | |
2156 | // Test cache invalidation. | |
2157 | $cache = cache::make('core', 'config'); | |
2158 | $this->assertInternalType('array', $cache->get('core')); | |
2159 | $this->assertInternalType('array', $cache->get('mod_forum')); | |
2160 | set_config('phpunit_test_get_config_1', 'test b'); | |
2161 | $this->assertFalse($cache->get('core')); | |
2162 | set_config('phpunit_test_get_config_4', 'test c', 'mod_forum'); | |
2163 | $this->assertFalse($cache->get('mod_forum')); | |
2164 | } | |
6b219310 | 2165 | |
be6ce11c | 2166 | public function test_get_max_upload_sizes() { |
6b219310 DW |
2167 | // Test with very low limits so we are not affected by php upload limits. |
2168 | // Test activity limit smallest. | |
2169 | $sitebytes = 102400; | |
2170 | $coursebytes = 51200; | |
2171 | $modulebytes = 10240; | |
2172 | $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes); | |
2173 | ||
be6ce11c PS |
2174 | $this->assertSame('Activity upload limit (10KB)', $result['0']); |
2175 | $this->assertCount(2, $result); | |
6b219310 DW |
2176 | |
2177 | // Test course limit smallest. | |
2178 | $sitebytes = 102400; | |
2179 | $coursebytes = 10240; | |
2180 | $modulebytes = 51200; | |
2181 | $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes); | |
2182 | ||
be6ce11c PS |
2183 | $this->assertSame('Course upload limit (10KB)', $result['0']); |
2184 | $this->assertCount(2, $result); | |
6b219310 DW |
2185 | |
2186 | // Test site limit smallest. | |
2187 | $sitebytes = 10240; | |
2188 | $coursebytes = 102400; | |
2189 | $modulebytes = 51200; | |
2190 | $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes); | |
2191 | ||
be6ce11c PS |
2192 | $this->assertSame('Site upload limit (10KB)', $result['0']); |
2193 | $this->assertCount(2, $result); | |
6b219310 DW |
2194 | |
2195 | // Test site limit not set. | |
2196 | $sitebytes = 0; | |
2197 | $coursebytes = 102400; | |
2198 | $modulebytes = 51200; | |
2199 | $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes); | |
2200 | ||
be6ce11c PS |
2201 | $this->assertSame('Activity upload limit (50KB)', $result['0']); |
2202 | $this->assertCount(3, $result); | |
6b219310 DW |
2203 | |
2204 | $sitebytes = 0; | |
2205 | $coursebytes = 51200; | |
2206 | $modulebytes = 102400; | |
2207 | $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes); | |
2208 | ||
be6ce11c PS |
2209 | $this->assertSame('Course upload limit (50KB)', $result['0']); |
2210 | $this->assertCount(3, $result); | |
6b219310 | 2211 | |
6b219310 DW |
2212 | // Test custom bytes in range. |
2213 | $sitebytes = 102400; | |
2214 | $coursebytes = 51200; | |
2215 | $modulebytes = 51200; | |
2216 | $custombytes = 10240; | |
2217 | $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes); | |
2218 | ||
be6ce11c | 2219 | $this->assertCount(3, $result); |
6b219310 DW |
2220 | |
2221 | // Test custom bytes in range but non-standard. | |
2222 | $sitebytes = 102400; | |
2223 | $coursebytes = 51200; | |
2224 | $modulebytes = 51200; | |
2225 | $custombytes = 25600; | |
2226 | $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes); | |
2227 | ||
be6ce11c | 2228 | $this->assertCount(4, $result); |
6b219310 DW |
2229 | |
2230 | // Test custom bytes out of range. | |
2231 | $sitebytes = 102400; | |
2232 | $coursebytes = 51200; | |
2233 | $modulebytes = 51200; | |
2234 | $custombytes = 102400; | |
2235 | $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes); | |
2236 | ||
be6ce11c | 2237 | $this->assertCount(3, $result); |
6b219310 DW |
2238 | |
2239 | // Test custom bytes out of range and non-standard. | |
2240 | $sitebytes = 102400; | |
2241 | $coursebytes = 51200; | |
2242 | $modulebytes = 51200; | |
2243 | $custombytes = 256000; | |
2244 | $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes); | |
2245 | ||
be6ce11c | 2246 | $this->assertCount(3, $result); |
f869cb23 PS |
2247 | |
2248 | // Test site limit only. | |
2249 | $sitebytes = 51200; | |
2250 | $result = get_max_upload_sizes($sitebytes); | |
2251 | ||
be6ce11c PS |
2252 | $this->assertSame('Site upload limit (50KB)', $result['0']); |
2253 | $this->assertSame('50KB', $result['51200']); | |
2254 | $this->assertSame('10KB', $result['10240']); | |
f869cb23 PS |
2255 | $this->assertCount(3, $result); |
2256 | ||
2257 | // Test no limit. | |
2258 | $result = get_max_upload_sizes(); | |
2259 | $this->assertArrayHasKey('0', $result); | |
2260 | $this->assertArrayHasKey(get_max_upload_file_size(), $result); | |
6b219310 | 2261 | } |
1e7db9fe | 2262 | |
ec2d8ceb SC |
2263 | /** |
2264 | * Test function password_is_legacy_hash(). | |
2265 | */ | |
2266 | public function test_password_is_legacy_hash() { | |
2267 | // Well formed md5s should be matched. | |
2268 | foreach (array('some', 'strings', 'to_check!') as $string) { | |
2269 | $md5 = md5($string); | |
2270 | $this->assertTrue(password_is_legacy_hash($md5)); | |
2271 | } | |
2272 | // Strings that are not md5s should not be matched. | |
2273 | foreach (array('', AUTH_PASSWORD_NOT_CACHED, 'IPW8WTcsWNgAWcUS1FBVHegzJnw5M2jOmYkmfc8z.xdBOyC4Caeum') as $notmd5) { | |
2274 | $this->assertFalse(password_is_legacy_hash($notmd5)); | |
2275 | } | |
2276 | } | |
2277 | ||
2278 | /** | |
2279 | * Test function validate_internal_user_password(). | |
2280 | */ | |
2281 | public function test_validate_internal_user_password() { | |
25294dd4 | 2282 | // Test bcrypt hashes. |
6780a1d3 PŠ |
2283 | $validhashes = array( |
2284 | 'pw' => '$2y$10$LOSDi5eaQJhutSRun.OVJ.ZSxQZabCMay7TO1KmzMkDMPvU40zGXK', | |
2285 | 'abc' => '$2y$10$VWTOhVdsBbWwtdWNDRHSpewjd3aXBQlBQf5rBY/hVhw8hciarFhXa', | |
2286 | 'C0mP1eX_&}<?@*&%` |\"' => '$2y$10$3PJf.q.9ywNJlsInPbqc8.IFeSsvXrGvQLKRFBIhVu1h1I3vpIry6', | |
2287 | 'ĩńťėŕňăţĩōŋāĹ' => '$2y$10$3A2Y8WpfRAnP3czJiSv6N.6Xp0T8hW3QZz2hUCYhzyWr1kGP1yUve' | |
2288 | ); | |
ec2d8ceb SC |
2289 | |
2290 | foreach ($validhashes as $password => $hash) { | |
2291 | $user = new stdClass(); | |
2292 | $user->auth = 'manual'; | |
2293 | $user->password = $hash; | |
2294 | // The correct password should be validated. | |
2295 | $this->assertTrue(validate_internal_user_password($user, $password)); | |
2296 | // An incorrect password should not be validated. | |
2297 | $this->assertFalse(validate_internal_user_password($user, 'badpw')); | |
2298 | } | |
2299 | } | |
2300 | ||
2301 | /** | |
2302 | * Test function hash_internal_user_password(). | |
2303 | */ | |
2304 | public function test_hash_internal_user_password() { | |
2305 | $passwords = array('pw', 'abc123', 'C0mP1eX_&}<?@*&%` |\"', 'ĩńťėŕňăţĩōŋāĹ'); | |
2306 | ||
2307 | // Check that some passwords that we convert to hashes can | |
2308 | // be validated. | |
2309 | foreach ($passwords as $password) { | |
2310 | $hash = hash_internal_user_password($password); | |
2311 | $fasthash = hash_internal_user_password($password, true); | |
2312 | $user = new stdClass(); | |
2313 | $user->auth = 'manual'; | |
2314 | $user->password = $hash; | |
2315 | $this->assertTrue(validate_internal_user_password($user, $password)); | |
2316 | ||
25294dd4 | 2317 | // They should not be in md5 format. |
6780a1d3 PŠ |
2318 | $this->assertFalse(password_is_legacy_hash($hash)); |
2319 | ||
2320 | // Check that cost factor in hash is correctly set. | |
2321 | $this->assertRegExp('/\$10\$/', $hash); | |
2322 | $this->assertRegExp('/\$04\$/', $fasthash); | |
ec2d8ceb SC |
2323 | } |
2324 | } | |
2325 | ||
2326 | /** | |
2327 | * Test function update_internal_user_password(). | |
2328 | */ | |
2329 | public function test_update_internal_user_password() { | |
2330 | global $DB; | |
2331 | $this->resetAfterTest(); | |
2332 | $passwords = array('password', '1234', 'changeme', '****'); | |
2333 | foreach ($passwords as $password) { | |
2334 | $user = $this->getDataGenerator()->create_user(array('auth'=>'manual')); | |
2335 | update_internal_user_password($user, $password); | |
2336 | // The user object should have been updated. | |
2337 | $this->assertTrue(validate_internal_user_password($user, $password)); | |
2338 | // The database field for the user should also have been updated to the | |
2339 | // same value. | |
be6ce11c | 2340 | $this->assertSame($user->password, $DB->get_field('user', 'password', array('id' => $user->id))); |
ec2d8ceb SC |
2341 | } |
2342 | ||
2343 | $user = $this->getDataGenerator()->create_user(array('auth'=>'manual')); | |
2344 | // Manually set the user's password to the md5 of the string 'password'. | |
2345 | $DB->set_field('user', 'password', '5f4dcc3b5aa765d61d8327deb882cf99', array('id' => $user->id)); | |
2346 | ||
a7466eb4 | 2347 | $sink = $this->redirectEvents(); |
ec2d8ceb SC |
2348 | // Update the password. |
2349 | update_internal_user_password($user, 'password'); | |
a7466eb4 RT |
2350 | $events = $sink->get_events(); |
2351 | $sink->close(); | |
2352 | $event = array_pop($events); | |
ec2d8ceb | 2353 | |
25294dd4 | 2354 | // Password should have been updated to a bcrypt hash. |
6780a1d3 | 2355 | $this->assertFalse(password_is_legacy_hash($user->password)); |
a7466eb4 RT |
2356 | |
2357 | // Verify event information. | |
2358 | $this->assertInstanceOf('\core\event\user_password_updated', $event); | |
2359 | $this->assertSame($user->id, $event->relateduserid); | |
2360 | $this->assertEquals(context_user::instance($user->id), $event->get_context()); | |
2361 | $this->assertEventContextNotUsed($event); | |
fb71171f MG |
2362 | |
2363 | // Verify recovery of property 'auth'. | |
2364 | unset($user->auth); | |
2365 | update_internal_user_password($user, 'newpassword'); | |
2366 | $this->assertDebuggingCalled('User record in update_internal_user_password() must include field auth', | |
2367 | DEBUG_DEVELOPER); | |
2368 | $this->assertEquals('manual', $user->auth); | |
ec2d8ceb | 2369 | } |
a327f25e | 2370 | |
b5bad169 MN |
2371 | /** |
2372 | * Testing that if the password is not cached, that it does not update | |
2373 | * the user table and fire event. | |
2374 | */ | |
2375 | public function test_update_internal_user_password_no_cache() { | |
2376 | $this->resetAfterTest(); | |
2377 | ||
2378 | $user = $this->getDataGenerator()->create_user(array('auth' => 'cas')); | |
2379 | $this->assertEquals(AUTH_PASSWORD_NOT_CACHED, $user->password); | |
2380 | ||
2381 | $sink = $this->redirectEvents(); | |
2382 | update_internal_user_password($user, 'wonkawonka'); | |
2383 | $this->assertEquals(0, $sink->count(), 'User updated event should not fire'); | |
2384 | } | |
2385 | ||
2386 | /** | |
2387 | * Test if the user has a password hash, but now their auth method | |
2388 | * says not to cache it. Then it should update. | |
2389 | */ | |
2390 | public function test_update_internal_user_password_update_no_cache() { | |
2391 | $this->resetAfterTest(); | |
2392 | ||
2393 | $user = $this->getDataGenerator()->create_user(array('password' => 'test')); | |
2394 | $this->assertNotEquals(AUTH_PASSWORD_NOT_CACHED, $user->password); | |
2395 | $user->auth = 'cas'; // Change to a auth that does not store passwords. | |
2396 | ||
2397 | $sink = $this->redirectEvents(); | |
2398 | update_internal_user_password($user, 'wonkawonka'); | |
2399 | $this->assertGreaterThanOrEqual(1, $sink->count(), 'User updated event should fire'); | |
2400 | ||
2401 | $this->assertEquals(AUTH_PASSWORD_NOT_CACHED, $user->password); | |
2402 | } | |
2403 | ||
a327f25e AG |
2404 | public function test_fullname() { |
2405 | global $CFG; | |
2406 | ||
2407 | $this->resetAfterTest(); | |
2408 | ||
2409 | // Create a user to test the name display on. | |
2410 | $record = array(); | |
2411 | $record['firstname'] = 'Scott'; | |
2412 | $record['lastname'] = 'Fletcher'; | |
2413 | $record['firstnamephonetic'] = 'スコット'; | |
2414 | $record['lastnamephonetic'] = 'フレチャー'; | |
2415 | $record['alternatename'] = 'No friends'; | |
2416 | $user = $this->getDataGenerator()->create_user($record); | |
2417 | ||
be6ce11c | 2418 | // Back up config settings for restore later. |
a327f25e AG |
2419 | $originalcfg = new stdClass(); |
2420 | $originalcfg->fullnamedisplay = $CFG->fullnamedisplay; | |
795c5881 | 2421 | $originalcfg->alternativefullnameformat = $CFG->alternativefullnameformat; |
a327f25e AG |
2422 | |
2423 | // Testing existing fullnamedisplay settings. | |
2424 | $CFG->fullnamedisplay = 'firstname'; | |
2425 | $testname = fullname($user); | |
be6ce11c | 2426 | $this->assertSame($user->firstname, $testname); |
a327f25e AG |
2427 | |
2428 | $CFG->fullnamedisplay = 'firstname lastname'; | |
2429 | $expectedname = "$user->firstname $user->lastname"; | |
2430 | $testname = fullname($user); | |
be6ce11c | 2431 | $this->assertSame($expectedname, $testname); |
a327f25e AG |
2432 | |
2433 | $CFG->fullnamedisplay = 'lastname firstname'; | |
2434 | $expectedname = "$user->lastname $user->firstname"; | |
2435 | $testname = fullname($user); | |
be6ce11c | 2436 | $this->assertSame($expectedname, $testname); |
a327f25e AG |
2437 | |
2438 | $expectedname = get_string('fullnamedisplay', null, $user); | |
2439 | $CFG->fullnamedisplay = 'language'; | |
2440 | $testname = fullname($user); | |
be6ce11c | 2441 | $this->assertSame($expectedname, $testname); |
a327f25e AG |
2442 | |
2443 | // Test override parameter. | |
2444 | $CFG->fullnamedisplay = 'firstname'; | |
2445 | $expectedname = "$user->firstname $user->lastname"; | |
2446 | $testname = fullname($user, true); | |
be6ce11c | 2447 | $this->assertSame($expectedname, $testname); |
a327f25e | 2448 | |
795c5881 AG |
2449 | // Test alternativefullnameformat setting. |
2450 | // Test alternativefullnameformat that has been set to nothing. | |
2451 | $CFG->alternativefullnameformat = ''; | |
2452 | $expectedname = "$user->firstname $user->lastname"; | |
2453 | $testname = fullname($user, true); | |
2454 | $this->assertSame($expectedname, $testname); | |
2455 | ||
2456 | // Test alternativefullnameformat that has been set to 'language'. | |
2457 | $CFG->alternativefullnameformat = 'language'; | |
2458 | $expectedname = "$user->firstname $user->lastname"; | |
2459 | $testname = fullname($user, true); | |
2460 | $this->assertSame($expectedname, $testname); | |
2461 | ||
2462 | // Test customising the alternativefullnameformat setting with all additional name fields. | |
2463 | $CFG->alternativefullnameformat = 'firstname lastname firstnamephonetic lastnamephonetic middlename alternatename'; | |
2464 | $expectedname = "$user->firstname $user->lastname $user->firstnamephonetic $user->lastnamephonetic $user->middlename $user->alternatename"; | |
2465 | $testname = fullname($user, true); | |
2466 | $this->assertSame($expectedname, $testname); | |
2467 | ||
a327f25e AG |
2468 | // Test additional name fields. |
2469 | $CFG->fullnamedisplay = 'lastname lastnamephonetic firstname firstnamephonetic'; | |
2470 | $expectedname = "$user->lastname $user->lastnamephonetic $user->firstname $user->firstnamephonetic"; | |
2471 | $testname = fullname($user); | |
be6ce11c | 2472 | $this->assertSame($expectedname, $testname); |
a327f25e AG |
2473 | |
2474 | // Test for handling missing data. | |
2475 | $user->middlename = null; | |
2476 | // Parenthesis with no data. | |
2477 | $CFG->fullnamedisplay = 'firstname (middlename) lastname'; | |
2478 | $expectedname = "$user->firstname $user->lastname"; | |
2479 | $testname = fullname($user); | |
be6ce11c | 2480 | $this->assertSame($expectedname, $testname); |
a327f25e AG |
2481 | |
2482 | // Extra spaces due to no data. | |
2483 | $CFG->fullnamedisplay = 'firstname middlename lastname'; | |
2484 | $expectedname = "$user->firstname $user->lastname"; | |
2485 | $testname = fullname($user); | |
be6ce11c | 2486 | $this->assertSame($expectedname, $testname); |
a327f25e AG |
2487 | |
2488 | // Regular expression testing. | |
be6ce11c | 2489 | // Remove some data from the user fields. |
a327f25e AG |
2490 | $user->firstnamephonetic = ''; |
2491 | $user->lastnamephonetic = ''; | |
2492 | ||
2493 | // Removing empty brackets and excess whitespace. | |
2494 | // All of these configurations should resolve to just firstname lastname. | |
2495 | $configarray = array(); | |
2496 | $configarray[] = 'firstname lastname [firstnamephonetic lastnamephonetic]'; | |
2497 | $configarray[] = 'firstname lastname \'middlename\''; | |
2498 | $configarray[] = 'firstname "firstnamephonetic" lastname'; | |
2499 | $configarray[] = 'firstname 「firstnamephonetic」 lastname 「lastnamephonetic」'; | |
2500 | ||
2501 | foreach ($configarray as $config) { | |
2502 | $CFG->fullnamedisplay = $config; | |
2503 | $expectedname = "$user->firstname $user->lastname"; | |
2504 | $testname = fullname($user); | |
be6ce11c | 2505 | $this->assertSame($expectedname, $testname); |
a327f25e AG |
2506 | } |
2507 | ||
2508 | // Check to make sure that other characters are left in place. | |
2509 | $configarray = array(); | |
2510 | $configarray['0'] = new stdClass(); | |
2511 | $configarray['0']->config = 'lastname firstname, middlename'; | |
2512 | $configarray['0']->expectedname = "$user->lastname $user->firstname,"; | |
2513 | $configarray['1'] = new stdClass(); | |
2514 | $configarray['1']->config = 'lastname firstname + alternatename'; | |
2515 | $configarray['1']->expectedname = "$user->lastname $user->firstname + $user->alternatename"; | |
2516 | $configarray['2'] = new stdClass(); | |
2517 | $configarray['2']->config = 'firstname aka: alternatename'; | |
2518 | $configarray['2']->expectedname = "$user->firstname aka: $user->alternatename"; | |
2519 | $configarray['3'] = new stdClass(); | |
2520 | $configarray['3']->config = 'firstname (alternatename)'; | |
2521 | $configarray['3']->expectedname = "$user->firstname ($user->alternatename)"; | |
2522 | $configarray['4'] = new stdClass(); | |
2523 | $configarray['4']->config = 'firstname [alternatename]'; | |
2524 | $configarray['4']->expectedname = "$user->firstname [$user->alternatename]"; | |
2525 | $configarray['5'] = new stdClass(); | |
2526 | $configarray['5']->config = 'firstname "lastname"'; | |
2527 | $configarray['5']->expectedname = "$user->firstname \"$user->lastname\""; | |
2528 | ||
2529 | foreach ($configarray as $config) { | |
2530 | $CFG->fullnamedisplay = $config->config; | |
2531 | $expectedname = $config->expectedname; | |
2532 | $testname = fullname($user); | |
be6ce11c | 2533 | $this->assertSame($expectedname, $testname); |
a327f25e AG |
2534 | } |
2535 | ||
73d363f0 AG |
2536 | // Test debugging message displays when |
2537 | // fullnamedisplay setting is "normal". | |
2538 | $CFG->fullnamedisplay = 'firstname lastname'; | |
2539 | unset($user); | |
2540 | $user = new stdClass(); | |
2541 | $user->firstname = 'Stan'; | |
2542 | $user->lastname = 'Lee'; | |
2543 | $namedisplay = fullname($user); | |
2544 | $this->assertDebuggingCalled(); | |
2545 | ||
be6ce11c | 2546 | // Tidy up after we finish testing. |
a327f25e | 2547 | $CFG->fullnamedisplay = $originalcfg->fullnamedisplay; |
795c5881 | 2548 | $CFG->alternativefullnameformat = $originalcfg->alternativefullnameformat; |
a327f25e AG |
2549 | } |
2550 | ||
2551 | public function test_get_all_user_name_fields() { | |
2552 | $this->resetAfterTest(); | |
2553 | ||
2554 | // Additional names in an array. | |
f490f988 AG |
2555 | $testarray = array('firstnamephonetic' => 'firstnamephonetic', |
2556 | 'lastnamephonetic' => 'lastnamephonetic', | |
2557 | 'middlename' => 'middlename', | |
2558 | 'alternatename' => 'alternatename', | |
2559 | 'firstname' => 'firstname', | |
2560 | 'lastname' => 'lastname'); | |
a327f25e AG |
2561 | $this->assertEquals($testarray, get_all_user_name_fields()); |
2562 | ||
2563 | // Additional names as a string. | |
2564 | $teststring = 'firstnamephonetic,lastnamephonetic,middlename,alternatename,firstname,lastname'; | |
2565 | $this->assertEquals($teststring, get_all_user_name_fields(true)); | |
2566 | ||
2567 | // Additional names as a string with an alias. | |
2568 | $teststring = 't.firstnamephonetic,t.lastnamephonetic,t.middlename,t.alternatename,t.firstname,t.lastname'; | |
2569 | $this->assertEquals($teststring, get_all_user_name_fields(true, 't')); | |
f490f988 | 2570 | |
5b1944bb | 2571 | // Additional name fields with a prefix - object. |
f490f988 AG |
2572 | $testarray = array('firstnamephonetic' => 'authorfirstnamephonetic', |
2573 | 'lastnamephonetic' => 'authorlastnamephonetic', | |
2574 | 'middlename' => 'authormiddlename', | |
2575 | 'alternatename' => 'authoralternatename', | |
2576 | 'firstname' => 'authorfirstname', | |
2577 | 'lastname' => 'authorlastname'); | |
2578 | $this->assertEquals($testarray, get_all_user_name_fields(false, null, 'author')); | |
2579 | ||
5b1944bb | 2580 | // Additional name fields with an alias and a title - string. |
f490f988 AG |
2581 | $teststring = 'u.firstnamephonetic AS authorfirstnamephonetic,u.lastnamephonetic AS authorlastnamephonetic,u.middlename AS authormiddlename,u.alternatename AS authoralternatename,u.firstname AS authorfirstname,u.lastname AS authorlastname'; |
2582 | $this->assertEquals($teststring, get_all_user_name_fields(true, 'u', null, 'author')); | |
e622b1a1 AG |
2583 | |
2584 | // Test the order parameter of the function. | |
2585 | // Returning an array. | |
2586 | $testarray = array('firstname' => 'firstname', | |
2587 | 'lastname' => 'lastname', | |
2588 | 'firstnamephonetic' => 'firstnamephonetic', | |
2589 | 'lastnamephonetic' => 'lastnamephonetic', | |
2590 | 'middlename' => 'middlename', | |
2591 | 'alternatename' => 'alternatename' | |
2592 | ); | |
2593 | $this->assertEquals($testarray, get_all_user_name_fields(false, null, null, null, true)); | |
2594 | ||
2595 | // Returning a string. | |
2596 | $teststring = 'firstname,lastname,firstnamephonetic,lastnamephonetic,middlename,alternatename'; | |
2597 | $this->assertEquals($teststring, get_all_user_name_fields(true, null, null, null, true)); | |
a327f25e AG |
2598 | } |
2599 | ||
2600 | public function test_order_in_string() { | |
2601 | $this->resetAfterTest(); | |
2602 | ||
2603 | // Return an array in an order as they are encountered in a string. | |
2604 | $valuearray = array('second', 'firsthalf', 'first'); | |
2605 | $formatstring = 'first firsthalf some other text (second)'; | |
2606 | $expectedarray = array('0' => 'first', '6' => 'firsthalf', '33' => 'second'); | |
2607 | $this->assertEquals($expectedarray, order_in_string($valuearray, $formatstring)); | |
2608 | ||
2609 | // Try again with a different order for the format. | |
2610 | $valuearray = array('second', 'firsthalf', 'first'); | |
2611 | $formatstring = 'firsthalf first second'; | |
2612 | $expectedarray = array('0' => 'firsthalf', '10' => 'first', '16' => 'second'); | |
2613 | $this->assertEquals($expectedarray, order_in_string($valuearray, $formatstring)); | |
2614 | ||
2615 | // Try again with yet another different order for the format. | |
2616 | $valuearray = array('second', 'firsthalf', 'first'); | |
2617 | $formatstring = 'start seconds away second firstquater first firsthalf'; | |
2618 | $expectedarray = array('19' => 'second', '38' => 'first', '44' => 'firsthalf'); | |
2619 | $this->assertEquals($expectedarray, order_in_string($valuearray, $formatstring)); | |
2620 | } | |
bb78e249 | 2621 | |
d79d5ac2 | 2622 | public function test_complete_user_login() { |
93b3aaf4 | 2623 | global $USER, $DB; |
d79d5ac2 PS |
2624 | |
2625 | $this->resetAfterTest(); | |
2626 | $user = $this->getDataGenerator()->create_user(); | |
2627 | $this->setUser(0); | |
2628 | ||
2629 | $sink = $this->redirectEvents(); | |
93b3aaf4 PS |
2630 | $loginuser = clone($user); |
2631 | $this->setCurrentTimeStart(); | |
2632 | @complete_user_login($loginuser); // Hide session header errors. | |
2633 | $this->assertSame($loginuser, $USER); | |
d79d5ac2 PS |
2634 | $this->assertEquals($user->id, $USER->id); |
2635 | $events = $sink->get_events(); | |
2636 | $sink->close(); | |
2637 | ||
54b1e040 PŠ |
2638 | $this->assertCount(1, $events); |
2639 | $event = reset($events); | |
d79d5ac2 PS |
2640 | $this->assertInstanceOf('\core\event\user_loggedin', $event); |
2641 | $this->assertEquals('user', $event->objecttable); | |
2642 | $this->assertEquals($user->id, $event->objectid); | |
2643 | $this->assertEquals(context_system::instance()->id, $event->contextid); | |
623a32e5 | 2644 | $this->assertEventContextNotUsed($event); |
93b3aaf4 PS |
2645 | |
2646 | $user = $DB->get_record('user', array('id'=>$user->id)); | |
2647 | ||
2648 | $this->assertTimeCurrent($user->firstaccess); | |
2649 | $this->assertTimeCurrent($user->lastaccess); | |
93b3aaf4 PS |
2650 | |
2651 | $this->assertTimeCurrent($USER->firstaccess); | |
2652 | $this->assertTimeCurrent($USER->lastaccess); | |
93b3aaf4 PS |
2653 | $this->assertTimeCurrent($USER->currentlogin); |
2654 | $this->assertSame(sesskey(), $USER->sesskey); | |
2655 | $this->assertTimeCurrent($USER->preference['_lastloaded']); | |
2656 | $this->assertObjectNotHasAttribute('password', $USER); | |
2657 | $this->assertObjectNotHasAttribute('description', $USER); | |
d79d5ac2 PS |
2658 | } |
2659 | ||
bb78e249 RT |
2660 | /** |
2661 | * Test require_logout. | |
2662 | */ | |
2663 | public function test_require_logout() { | |
2664 | $this->resetAfterTest(); | |
2665 | $user = $this->getDataGenerator()->create_user(); | |
2666 | $this->setUser($user); | |
bb78e249 RT |
2667 | |
2668 | $this->assertTrue(isloggedin()); | |
2669 | ||
2670 | // Logout user and capture event. | |
2671 | $sink = $this->redirectEvents(); | |
2672 | require_logout(); | |
2673 | $events = $sink->get_events(); | |
2674 | $sink->close(); | |
2675 | $event = array_pop($events); | |
2676 | ||
2677 | // Check if user is logged out. | |
2678 | $this->assertFalse(isloggedin()); | |
2679 | ||
2680 | // Test Event. | |
2681 | $this->assertInstanceOf('\core\event\user_loggedout', $event); | |
2682 | $this->assertSame($user->id, $event->objectid); | |
2683 | $this->assertSame('user_logout', $event->get_legacy_eventname()); | |
2684 | $this->assertEventLegacyData($user, $event); | |
2685 | $expectedlogdata = array(SITEID, 'user', 'logout', 'view.php?id='.$event->objectid.'&course='.SITEID, $event->objectid, 0, | |
2686 | $event->objectid); | |
2687 | $this->assertEventLegacyLogData($expectedlogdata, $event); | |
623a32e5 | 2688 | $this->assertEventContextNotUsed($event); |
bb78e249 | 2689 | } |
90930acf | 2690 | |
54dceeed BH |
2691 | /** |
2692 | * A data provider for testing email messageid | |
2693 | */ | |
2694 | public function generate_email_messageid_provider() { | |
2695 | return array( | |
2696 | 'nopath' => array( | |
2697 | 'wwwroot' => 'http://www.example.com', | |
2698 | 'ids' => array( | |
2699 | 'a-custom-id' => '<a-custom-id@www.example.com>', | |
2700 | 'an-id-with-/-a-slash' => '<an-id-with-%2F-a-slash@www.example.com>', | |
2701 | ), | |
2702 | ), | |
2703 | 'path' => array( | |
2704 | 'wwwroot' => 'http://www.example.com/path/subdir', | |
2705 | 'ids' => array( | |
2706 | 'a-custom-id' => '<a-custom-id/path/subdir@www.example.com>', | |
2707 | 'an-id-with-/-a-slash' => '<an-id-with-%2F-a-slash/path/subdir@www.example.com>', | |
2708 | ), | |
2709 | ), | |
2710 | ); | |
2711 | } | |
2712 | ||
2713 | /** | |
2714 | * Test email message id generation | |
2715 | * | |
2716 | * @dataProvider generate_email_messageid_provider | |
2717 | * | |
2718 | * @param string $wwwroot The wwwroot | |
2719 | * @param array $msgids An array of msgid local parts and the final result | |
2720 | */ | |
2721 | public function test_generate_email_messageid($wwwroot, $msgids) { | |
2722 | global $CFG; | |
2723 | ||
2724 | $this->resetAfterTest(); | |
2725 | $CFG->wwwroot = $wwwroot; | |
2726 | ||
2727 | foreach ($msgids as $local => $final) { | |
2728 | $this->assertEquals($final, generate_email_messageid($local)); | |
2729 | } | |
2730 | } | |
eca8cf67 | 2731 | |
34417803 BH |
2732 | /** |
2733 | * A data provider for testing email diversion | |
2734 | */ | |
eca8cf67 BH |
2735 | public function diverted_emails_provider() { |
2736 | return array( | |
2737 | 'nodiverts' => array( | |
2738 | 'divertallemailsto' => null, | |
2739 | 'divertallemailsexcept' => null, | |
2740 | array( | |
2741 | 'foo@example.com', | |
2742 | 'test@real.com', | |
2743 | 'fred.jones@example.com', | |
2744 | 'dev1@dev.com', | |
2745 | 'fred@example.com', | |
2746 | 'fred+verp@example.com', | |
2747 | ), | |
2748 | false, | |
2749 | ), | |
2750 | 'alldiverts' => array( | |
2751 | 'divertallemailsto' => 'somewhere@elsewhere.com', | |
2752 | 'divertallemailsexcept' => null, | |
2753 | array( | |
2754 | 'foo@example.com', | |
2755 | 'test@real.com', | |
2756 | 'fred.jones@example.com', | |
2757 | 'dev1@dev.com', | |
2758 | 'fred@example.com', | |
2759 | 'fred+verp@example.com', | |
2760 | ), | |
2761 | true, | |
2762 | ), | |
2763 | 'alsodiverts' => array( | |
2764 | 'divertallemailsto' => 'somewhere@elsewhere.com', | |
2765 | 'divertallemailsexcept' => '@dev.com, fred(\+.*)?@example.com', | |
2766 | array( | |
2767 | 'foo@example.com', | |
2768 | 'test@real.com', | |
2769 | 'fred.jones@example.com', | |
2770 | ), | |
2771 | true, | |
2772 | ), | |
2773 | 'divertsexceptions' => array( | |
2774 | 'divertallemailsto' => 'somewhere@elsewhere.com', | |
2775 | 'divertallemailsexcept' => '@dev.com, fred(\+.*)?@example.com', | |
2776 | array( | |
2777 | 'dev1@dev.com', | |
2778 | 'fred@example.com', | |
2779 | 'fred+verp@example.com', | |
2780 | ), | |
2781 | false, | |
2782 | ), | |
2783 | ); | |
2784 | } | |
2785 | ||
2786 | /** | |
34417803 BH |
2787 | * Test email diversion |
2788 | * | |
eca8cf67 | 2789 | * @dataProvider diverted_emails_provider |
34417803 BH |
2790 | * |
2791 | * @param string $divertallemailsto An optional email address | |
2792 | * @param string $divertallemailsexcept An optional exclusion list | |
2793 | * @param array $addresses An array of test addresses | |
2794 | * @param boolean $expected Expected result | |
eca8cf67 BH |
2795 | */ |
2796 | public function test_email_should_be_diverted($divertallemailsto, $divertallemailsexcept, $addresses, $expected) { | |
2797 | global $CFG; | |
2798 | ||
2799 | $this->resetAfterTest(); | |
2800 | $CFG->divertallemailsto = $divertallemailsto; | |
2801 | $CFG->divertallemailsexcept = $divertallemailsexcept; | |
2802 | ||
2803 | foreach ($addresses as $address) { | |
2804 | $this->assertEquals($expected, email_should_be_diverted($address)); | |
2805 | } | |
2806 | } | |
2807 | ||
90930acf | 2808 | public function test_email_to_user() { |
f007e899 PS |
2809 | global $CFG; |
2810 | ||
90930acf PS |
2811 | $this->resetAfterTest(); |
2812 | ||
e0789335 AG |
2813 | $user1 = $this->getDataGenerator()->create_user(array('maildisplay' => 1)); |
2814 | $user2 = $this->getDataGenerator()->create_user(array('maildisplay' => 1)); | |
2815 | $user3 = $this->getDataGenerator()->create_user(array('maildisplay' => 0)); | |
9e44cec2 | 2816 | set_config('allowedemaildomains', "example.com\r\nmoodle.org"); |
90930acf PS |
2817 | |
2818 | $subject = 'subject'; | |
2819 | $messagetext = 'message text'; | |
2820 | $subject2 = 'subject 2'; | |
2821 | $messagetext2 = 'message text 2'; | |
2822 | ||
fc05c1b8 AN |
2823 | // Close the default email sink. |
2824 | $sink = $this->redirectEmails(); | |
2825 | $sink->close(); | |
2826 | ||
2827 | $CFG->noemailever = true; | |
f007e899 PS |
2828 | $this->assertNotEmpty($CFG->noemailever); |
2829 | email_to_user($user1, $user2, $subject, $messagetext); | |
2830 | $this->assertDebuggingCalled('Not sending email due to $CFG->noemailever config setting'); | |
2831 | ||
90930acf PS |
2832 | unset_config('noemailever'); |
2833 | ||
f007e899 PS |
2834 | email_to_user($user1, $user2, $subject, $messagetext); |
2835 | $this->assertDebuggingCalled('Unit tests must not send real emails! Use $this->redirectEmails()'); | |
2836 | ||
90930acf PS |
2837 | $sink = $this->redirectEmails(); |
2838 | email_to_user($user1, $user2, $subject, $messagetext); | |
2839 | email_to_user($user2, $user1, $subject2, $messagetext2); | |
2840 | $this->assertSame(2, $sink->count()); | |
2841 | $result = $sink->get_messages(); | |
2842 | $this->assertCount(2, $result); | |
2843 | $sink->close(); | |
2844 | ||
2845 | $this->assertSame($subject, $result[0]->subject); | |
2846 | $this->assertSame($messagetext, trim($result[0]->body)); | |
2847 | $this->assertSame($user1->email, $result[0]->to); | |
2848 | $this->assertSame($user2->email, $result[0]->from); | |
2849 | ||
2850 | $this->assertSame($subject2, $result[1]->subject); | |
2851 | $this->assertSame($messagetext2, trim($result[1]->body)); | |
2852 | $this->assertSame($user2->email, $result[1]->to); | |
2853 | $this->assertSame($user1->email, $result[1]->from); | |
f007e899 PS |
2854 | |
2855 | email_to_user($user1, $user2, $subject, $messagetext); | |
2856 | $this->assertDebuggingCalled('Unit tests must not send real emails! Use $this->redirectEmails()'); | |
85614920 | 2857 | |
e0789335 AG |
2858 | // Test that an empty noreplyaddress will default to a no-reply address. |
2859 | $sink = $this->redirectEmails(); | |
2860 | email_to_user($user1, $user3, $subject, $messagetext); | |
2861 | $result = $sink->get_messages(); | |
2862 | $this->assertEquals($CFG->noreplyaddress, $result[0]->from); | |
2863 | $sink->close(); | |
2864 | set_config('noreplyaddress', ''); | |
2865 | $sink = $this->redirectEmails(); | |
2866 | email_to_user($user1, $user3, $subject, $messagetext); | |
2867 | $result = $sink->get_messages(); | |
2868 | $this->assertEquals('noreply@www.example.com', $result[0]->from); | |
2869 | $sink->close(); | |
2870 | ||
2871 | // Test $CFG->allowedemaildomains. | |
2872 | set_config('noreplyaddress', 'noreply@www.example.com'); | |
2873 | $this->assertNotEmpty($CFG->allowedemaildomains); | |
85614920 MA |
2874 | $sink = $this->redirectEmails(); |
2875 | email_to_user($user1, $user2, $subject, $messagetext); | |
e0789335 | 2876 | unset_config('allowedemaildomains'); |
85614920 MA |
2877 | email_to_user($user1, $user2, $subject, $messagetext); |
2878 | $result = $sink->get_messages(); | |
e0789335 AG |
2879 | $this->assertNotEquals($CFG->noreplyaddress, $result[0]->from); |
2880 | $this->assertEquals($CFG->noreplyaddress, $result[1]->from); | |
85614920 | 2881 | $sink->close(); |
62b48293 JD |
2882 | |
2883 | // Try to send an unsafe attachment, we should see an error message in the eventual mail body. | |
2884 | $attachment = '../test.txt'; | |
2885 | $attachname = 'txt'; | |
2886 | ||
2887 | $sink = $this->redirectEmails(); | |
2888 | email_to_user($user1, $user2, $subject, $messagetext, '', $attachment, $attachname); | |
2889 | $this->assertSame(1, $sink->count()); | |
2890 | $result = $sink->get_messages(); | |
2891 | $this->assertCount(1, $result); | |
2892 | $this->assertContains('error.txt', $result[0]->body); | |
2893 | $this->assertContains('Error in attachment. User attempted to attach a filename with a unsafe name.', $result[0]->body); | |
2894 | $sink->close(); | |
90930acf | 2895 | } |
6c679d15 AA |
2896 | |
2897 | /** | |
a7466eb4 | 2898 | * Test setnew_password_and_mail. |
6c679d15 | 2899 | */ |
a7466eb4 | 2900 | public function test_setnew_password_and_mail() { |
6c679d15 AA |
2901 | global $DB, $CFG; |
2902 | ||
2903 | $this->resetAfterTest(); | |
2904 | ||
2905 | $user = $this->getDataGenerator()->create_user(); | |
2906 | ||
6c679d15 AA |
2907 | // Update user password. |
2908 | $sink = $this->redirectEvents(); | |
2909 | $sink2 = $this->redirectEmails(); // Make sure we are redirecting emails. | |
2910 | setnew_password_and_mail($user); | |
6c679d15 AA |
2911 | $events = $sink->get_events(); |
2912 | $sink->close(); | |
2913 | $sink2->close(); | |
a7466eb4 | 2914 | $event = array_pop($events); |
6c679d15 AA |
2915 | |
2916 | // Test updated value. | |
2917 | $dbuser = $DB->get_record('user', array('id' => $user->id)); | |
2918 | $this->assertSame($user->firstname, $dbuser->firstname); | |
a7466eb4 | 2919 | $this->assertNotEmpty($dbuser->password); |
6c679d15 AA |
2920 | |
2921 | // Test event. | |
a7466eb4 RT |
2922 | $this->assertInstanceOf('\core\event\user_password_updated', $event); |
2923 | $this->assertSame($user->id, $event->relateduserid); | |
2924 | $this->assertEquals(context_user::instance($user->id), $event->get_context()); | |
2925 | $this->assertEventContextNotUsed($event); | |
6c679d15 AA |
2926 | } |
2927 | ||
a6db4a68 JO |
2928 | /** |
2929 | * Data provider for test_generate_confirmation_link | |
2930 | * @return Array of confirmation urls and expected resultant confirmation links | |
2931 | */ | |
2932 | public function generate_confirmation_link_provider() { | |
2933 | global $CFG; | |
2934 | return [ | |
2935 | "Simple name" => [ | |
2936 | "username" => "simplename", | |
2937 | "confirmationurl" => null, | |
2938 | "expected" => $CFG->wwwroot . "/login/confirm.php?data=/simplename" | |
2939 | ], | |
2940 | "Period in between words in username" => [ | |
2941 | "username" => "period.inbetween", | |
2942 | "confirmationurl" => null, | |
2943 | "expected" => $CFG->wwwroot . "/login/confirm.php?data=/period%2Einbetween" | |
2944 | ], | |
2945 | "Trailing periods in username" => [ | |
2946 | "username" => "trailingperiods...", | |
2947 | "confirmationurl" => null, | |
2948 | "expected" => $CFG->wwwroot . "/login/confirm.php?data=/trailingperiods%2E%2E%2E" | |
2949 | ], | |
2950 | "At symbol in username" => [ | |
2951 | "username" => "at@symbol", | |
2952 | "confirmationurl" => null, | |
2953 | "expected" => $CFG->wwwroot . "/login/confirm.php?data=/at%40symbol" | |
2954 | ], | |
2955 | "Dash symbol in username" => [ | |
2956 | "username" => "has-dash", | |
2957 | "confirmationurl" => null, | |
2958 | "expected" => $CFG->wwwroot . "/login/confirm.php?data=/has-dash" | |
2959 | ], | |
2960 | "Underscore in username" => [ | |
2961 | "username" => "under_score", | |
2962 | "confirmationurl" => null, | |
2963 | "expected" => $CFG->wwwroot . "/login/confirm.php?data=/under_score" | |
2964 | ], | |
2965 | "Many different characters in username" => [ | |
2966 | "username" => "many_-.@characters@_@-..-..", | |
2967 | "confirmationurl" => null, | |
2968 | "expected" => $CFG->wwwroot . "/login/confirm.php?data=/many_-%2E%40characters%40_%40-%2E%2E-%2E%2E" | |
2969 | ], | |
2970 | "Custom relative confirmation url" => [ | |
2971 | "username" => "many_-.@characters@_@-..-..", | |
2972 | "confirmationurl" => "/custom/local/url.php", | |
2973 | "expected" => $CFG->wwwroot . "/custom/local/url.php?data=/many_-%2E%40characters%40_%40-%2E%2E-%2E%2E" | |
2974 | ], | |
2975 | "Custom relative confirmation url with parameters" => [ | |
2976 | "username" => "many_-.@characters@_@-..-..", | |
2977 | "confirmationurl" => "/custom/local/url.php?with=param", | |
2978 | "expected" => $CFG->wwwroot . "/custom/local/url.php?with=param&data=/many_-%2E%40characters%40_%40-%2E%2E-%2E%2E" | |
2979 | ], | |
2980 | "Custom local confirmation url" => [ | |
2981 | "username" => "many_-.@characters@_@-..-..", | |
2982 | "confirmationurl" => $CFG->wwwroot . "/custom/local/url.php", | |
2983 | "expected" => $CFG->wwwroot . "/custom/local/url.php?data=/many_-%2E%40characters%40_%40-%2E%2E-%2E%2E" | |
2984 | ], | |
2985 | "Custom local confirmation url with parameters" => [ | |
2986 | "username" => "many_-.@characters@_@-..-..", | |
2987 | "confirmationurl" => $CFG->wwwroot . "/custom/local/url.php?with=param", | |
2988 | "expected" => $CFG->wwwroot . "/custom/local/url.php?with=param&data=/many_-%2E%40characters%40_%40-%2E%2E-%2E%2E" | |
2989 | ], | |
2990 | "Custom external confirmation url" => [ | |
2991 | "username" => "many_-.@characters@_@-..-..", | |
2992 | "confirmationurl" => "http://moodle.org/custom/external/url.php", | |
2993 | "expected" => "http://moodle.org/custom/external/url.php?data=/many_-%2E%40characters%40_%40-%2E%2E-%2E%2E" | |
2994 | ], | |
2995 | "Custom external confirmation url with parameters" => [ | |
2996 | "username" => "many_-.@characters@_@-..-..", | |
2997 | "confirmationurl" => "http://moodle.org/ext.php?with=some¶m=eters", | |
2998 | "expected" => "http://moodle.org/ext.php?with=some¶m=eters&data=/many_-%2E%40characters%40_%40-%2E%2E-%2E%2E" | |
2999 | ], | |
3000 | "Custom external confirmation url with parameters" => [ | |
3001 | "username" => "many_-.@characters@_@-..-..", | |
3002 | "confirmationurl" => "http://moodle.org/ext.php?with=some&data=test", | |
3003 | "expected" => "http://moodle.org/ext.php?with=some&data=/many_-%2E%40characters%40_%40-%2E%2E-%2E%2E" | |
3004 | ], | |
3005 | ]; | |
3006 | } | |
3007 | ||
3008 | /** | |
3009 | * Test generate_confirmation_link | |
3010 | * @dataProvider generate_confirmation_link_provider | |
3011 | * @param string $username The name of the user | |
3012 | * @param string $confirmationurl The url the user should go to to confirm | |
3013 | * @param string $expected The expected url of the confirmation link | |
3014 | */ | |
3015 | public function test_generate_confirmation_link($username, $confirmationurl, $expected) { | |
3016 | $this->resetAfterTest(); | |
3017 | $sink = $this->redirectEmails(); | |
3018 | ||
3019 | $user = $this->getDataGenerator()->create_user( | |
3020 | [ | |
3021 | "username" => $username, | |
3022 | "confirmed" => false, | |
3023 | "email" => 'test@example.com', | |
3024 | ] | |
3025 | ); | |
3026 | ||
3027 | send_confirmation_email($user, $confirmationurl); | |
3028 | $sink->close(); | |
3029 | $messages = $sink->get_messages(); | |
3030 | $message = array_shift($messages); | |
3031 | $messagebody = quoted_printable_decode($message->body); | |
3032 | ||
3033 | $this->assertContains($expected, $messagebody); | |
3034 | } | |
3035 | ||
3036 | /** | |
3037 | * Test generate_confirmation_link with custom admin link | |
3038 | */ | |
3039 | public function test_generate_confirmation_link_with_custom_admin() { | |
3040 | global $CFG; | |
3041 | ||
3042 | $this->resetAfterTest(); | |
3043 | $sink = $this->redirectEmails(); | |
3044 | ||
3045 | $admin = $CFG->admin; | |
3046 | $CFG->admin = 'custom/admin/path'; | |
3047 | ||
3048 | $user = $this->getDataGenerator()->create_user( | |
3049 | [ | |
3050 | "username" => "many_-.@characters@_@-..-..", | |
3051 | "confirmed" => false, | |
3052 | "email" => 'test@example.com', | |
3053 | ] | |
3054 | ); | |
3055 | $confirmationurl = "/admin/test.php?with=params"; | |
3056 | $expected = $CFG->wwwroot . "/" . $CFG->admin . "/test.php?with=params&data=/many_-%2E%40characters%40_%40-%2E%2E-%2E%2E"; | |
3057 | ||
3058 | send_confirmation_email($user, $confirmationurl); | |
3059 | $sink->close(); | |
3060 | $messages = $sink->get_messages(); | |
3061 | $message = array_shift($messages); | |
3062 | $messagebody = quoted_printable_decode($message->body); | |
3063 | ||
3064 | $sink->close(); | |
3065 | $this->assertContains($expected, $messagebody); | |
3066 | ||
3067 | $CFG->admin = $admin; | |
3068 | } | |
3069 | ||
3070 | ||
cc49a6f7 AA |
3071 | /** |
3072 | * Test remove_course_content deletes course contents | |
3073 | * TODO Add asserts to verify other data related to course is deleted as well. | |
3074 | */ | |
3075 | public function test_remove_course_contents() { | |
3076 | ||
3077 | $this->resetAfterTest(); | |
3078 | ||
3079 | $course = $this->getDataGenerator()->create_course(); | |
3080 | $user = $this->getDataGenerator()->create_user(); | |
3081 | $gen = $this->getDataGenerator()->get_plugin_generator('core_notes'); | |
3082 | $note = $gen->create_instance(array('courseid' => $course->id, 'userid' => $user->id)); | |
3083 | ||
3084 | $this->assertNotEquals(false, note_load($note->id)); | |
3085 | remove_course_contents($course->id, false); | |
3086 | $this->assertFalse(note_load($note->id)); | |
3087 | } | |
3088 | ||
f490f988 AG |
3089 | /** |
3090 | * Test function username_load_fields_from_object(). | |
3091 | */ | |
3092 | public function test_username_load_fields_from_object() { | |
3093 | $this->resetAfterTest(); | |
3094 | ||
3095 | // This object represents the information returned from an sql query. | |
3096 | $userinfo = new stdClass(); | |
3097 | $userinfo->userid = 1; | |
3098 | $userinfo->username = 'loosebruce'; | |
3099 | $userinfo->firstname = 'Bruce'; | |
3100 | $userinfo->lastname = 'Campbell'; | |
3101 | $userinfo->firstnamephonetic = 'ブルース'; | |
3102 | $userinfo->lastnamephonetic = 'カンベッル'; | |
3103 | $userinfo->middlename = ''; | |