996a45f26f4ef46bbc3a35924c22dedf3dded367
[moodle.git] / lib / tests / moodlelib_test.php
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/>.
17 /**
18  * Unit tests for (some of) ../moodlelib.php.
19  *
20  * @package    core
21  * @category   phpunit
22  * @copyright  &copy; 2006 The Open University
23  * @author     T.J.Hunt@open.ac.uk
24  * @author     nicolas@moodle.com
25  */
27 defined('MOODLE_INTERNAL') || die();
29 class core_moodlelib_testcase extends advanced_testcase {
31     public static $includecoverage = array('lib/moodlelib.php');
33     protected $user_agents = array(
34         'MSIE' => array(
35             '5.0' => array('Windows 98' => 'Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)'),
36             '5.5' => array('Windows 2000' => 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)'),
37             '6.0' => array('Windows XP SP2' => 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)'),
38             '7.0' => array('Windows XP SP2' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; YPC 3.0.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)'),
39             '8.0' => array('Windows Vista' => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)'),
40             '9.0' => array('Windows 7' => 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)'),
41             '9.0i' => array('Windows 7' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)'),
42             '10.0' => array('Windows 8' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0; Touch)'),
43             '10.0i' => array('Windows 8' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.2; Trident/6.0; Touch; .NET4.0E; .NET4.0C; Tablet PC 2.0)'),
44         ),
45         'Firefox' => array(
46             '1.0.6'   => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.10) Gecko/20050716 Firefox/1.0.6'),
47             '1.5'     => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; nl; rv:1.8) Gecko/20051107 Firefox/1.5'),
48             '1.5.0.1' => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1'),
49             '2.0'     => array('Windows XP' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1',
50                                'Ubuntu Linux AMD64' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.8.1) Gecko/20060601 Firefox/2.0 (Ubuntu-edgy)'),
51             '3.0.6'   => array('SUSE' => 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.0.6) Gecko/2009012700 SUSE/3.0.6-1.4 Firefox/3.0.6'),
52             '3.6'     => array('Linux' => 'Mozilla/5.0 (X11; Linux i686; rv:2.0) Gecko/20100101 Firefox/3.6'),
53             '11.0'    => array('Windows' => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:11.0) Gecko Firefox/11.0'),
54             '15.0a2'  => array('Windows' => 'Mozilla/5.0 (Windows NT 6.1; rv:15.0) Gecko/20120716 Firefox/15.0a2'),
55             '18.0'    => array('Mac OS X' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:18.0) Gecko/18.0 Firefox/18.0'),
56         ),
57         'SeaMonkey' => array(
58             '2.0' => array('Windows' => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1b3pre) Gecko/20081208 SeaMonkey/2.0'),
59             '2.1' => array('Linux' => 'Mozilla/5.0 (X11; Linux x86_64; rv:2.0.1) Gecko/20110609 Firefox/4.0.1 SeaMonkey/2.1'),
60             '2.3' => array('FreeBSD' => 'Mozilla/5.0 (X11; FreeBSD amd64; rv:6.0) Gecko/20110818 Firefox/6.0 SeaMonkey/2.3'),
61         ),
62         'Safari' => array(
63             '312' => array('Mac OS X' => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/312.1 (KHTML, like Gecko) Safari/312'),
64             '412' => array('Mac OS X' => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/412 (KHTML, like Gecko) Safari/412')
65         ),
66         'Safari iOS' => array(
67             '528' => array('iPhone' => 'Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; cs-cz) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7D11 Safari/528.16'),
68             '533' => array('iPad' => 'Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5'),
69         ),
70         'WebKit Android' => array(
71             '525' => array('G1 Phone' => 'Mozilla/5.0 (Linux; U; Android 1.1; en-gb; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2 – G1 Phone'),
72             '530' => array('Nexus' => 'Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17 –Nexus'),
73         ),
74         'Chrome' => array(
75             '8' => array('Mac OS X' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_5; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.215 Safari/534.10'),
76         ),
77         'Opera' => array(
78             '8.51' => array('Windows XP' => 'Opera/8.51 (Windows NT 5.1; U; en)'),
79             '9.0'  => array('Windows XP' => 'Opera/9.0 (Windows NT 5.1; U; en)',
80                 'Debian Linux' => 'Opera/9.01 (X11; Linux i686; U; en)')
81         )
82     );
84     /**
85      * Define a local decimal separator.
86      *
87      * It is not possible to directly change the result of get_string in
88      * a unit test. Instead, we create a language pack for language 'xx' in
89      * dataroot and make langconfig.php with the string we need to change.
90      * The example separator used here is 'X'; on PHP 5.3 and before this
91      * must be a single byte character due to PHP bug/limitation in
92      * number_format, so you can't use UTF-8 characters.
93      */
94     protected function define_local_decimal_separator() {
95         global $SESSION, $CFG;
97         $SESSION->lang = 'xx';
98         $langconfig = "<?php\n\$string['decsep'] = 'X';";
99         $langfolder = $CFG->dataroot . '/lang/xx';
100         check_dir_exists($langfolder);
101         file_put_contents($langfolder . '/langconfig.php', $langconfig);
102     }
104     public function test_cleanremoteaddr() {
105         // IPv4.
106         $this->assertNull(cleanremoteaddr('1023.121.234.1'));
107         $this->assertSame('123.121.234.1', cleanremoteaddr('123.121.234.01 '));
109         // IPv6.
110         $this->assertNull(cleanremoteaddr('0:0:0:0:0:0:0:0:0'));
111         $this->assertNull(cleanremoteaddr('0:0:0:0:0:0:0:abh'));
112         $this->assertNull(cleanremoteaddr('0:0:0:::0:0:1'));
113         $this->assertSame('::', cleanremoteaddr('0:0:0:0:0:0:0:0', true));
114         $this->assertSame('::1:1', cleanremoteaddr('0:0:0:0:0:0:1:1', true));
115         $this->assertSame('abcd:ef::', cleanremoteaddr('abcd:00ef:0:0:0:0:0:0', true));
116         $this->assertSame('1::1', cleanremoteaddr('1:0:0:0:0:0:0:1', true));
117         $this->assertSame('0:0:0:0:0:0:10:1', cleanremoteaddr('::10:1', false));
118         $this->assertSame('1:1:0:0:0:0:0:0', cleanremoteaddr('01:1::', false));
119         $this->assertSame('10:0:0:0:0:0:0:10', cleanremoteaddr('10::10', false));
120         $this->assertSame('::ffff:c0a8:11', cleanremoteaddr('::ffff:192.168.1.1', true));
121     }
123     public function test_address_in_subnet() {
124         // 1: xxx.xxx.xxx.xxx/nn or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/nnn (number of bits in net mask).
125         $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.1/32'));
126         $this->assertFalse(address_in_subnet('123.121.23.1', '123.121.23.0/32'));
127         $this->assertTrue(address_in_subnet('10.10.10.100',  '123.121.23.45/0'));
128         $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/24'));
129         $this->assertFalse(address_in_subnet('123.121.34.1', '123.121.234.0/24'));
130         $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/30'));
131         $this->assertFalse(address_in_subnet('123.121.23.8', '123.121.23.0/30'));
132         $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128'));
133         $this->assertFalse(address_in_subnet('bab:baba::baba', 'bab:baba::cece/128'));
134         $this->assertTrue(address_in_subnet('baba:baba::baba', 'cece:cece::cece/0'));
135         $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128'));
136         $this->assertTrue(address_in_subnet('baba:baba::00ba', 'baba:baba::/120'));
137         $this->assertFalse(address_in_subnet('baba:baba::aba', 'baba:baba::/120'));
138         $this->assertTrue(address_in_subnet('baba::baba:00ba', 'baba::baba:0/112'));
139         $this->assertFalse(address_in_subnet('baba::aba:00ba', 'baba::baba:0/112'));
140         $this->assertFalse(address_in_subnet('aba::baba:0000', 'baba::baba:0/112'));
142         // Fixed input.
143         $this->assertTrue(address_in_subnet('123.121.23.1   ', ' 123.121.23.0 / 24'));
144         $this->assertTrue(address_in_subnet('::ffff:10.1.1.1', ' 0:0:0:000:0:ffff:a1:10 / 126'));
146         // Incorrect input.
147         $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/-2'));
148         $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/64'));
149         $this->assertFalse(address_in_subnet('123.121.234.x', '123.121.234.1/24'));
150         $this->assertFalse(address_in_subnet('123.121.234.0', '123.121.234.xx/24'));
151         $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/xx0'));
152         $this->assertFalse(address_in_subnet('::1', '::aa:0/xx0'));
153         $this->assertFalse(address_in_subnet('::1', '::aa:0/-5'));
154         $this->assertFalse(address_in_subnet('::1', '::aa:0/130'));
155         $this->assertFalse(address_in_subnet('x:1', '::aa:0/130'));
156         $this->assertFalse(address_in_subnet('::1', '::ax:0/130'));
158         // 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).
159         $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12-14'));
160         $this->assertTrue(address_in_subnet('123.121.234.13', '123.121.234.12-14'));
161         $this->assertTrue(address_in_subnet('123.121.234.14', '123.121.234.12-14'));
162         $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.12-14'));
163         $this->assertFalse(address_in_subnet('123.121.234.20', '123.121.234.12-14'));
164         $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.234.12-14'));
165         $this->assertFalse(address_in_subnet('123.12.234.12', '123.121.234.12-14'));
166         $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba-babe'));
167         $this->assertTrue(address_in_subnet('baba:baba::babc', 'baba:baba::baba-babe'));
168         $this->assertTrue(address_in_subnet('baba:baba::babe', 'baba:baba::baba-babe'));
169         $this->assertFalse(address_in_subnet('bab:baba::bab0', 'bab:baba::baba-babe'));
170         $this->assertFalse(address_in_subnet('bab:baba::babf', 'bab:baba::baba-babe'));
171         $this->assertFalse(address_in_subnet('bab:baba::bfbe', 'bab:baba::baba-babe'));
172         $this->assertFalse(address_in_subnet('bfb:baba::babe', 'bab:baba::baba-babe'));
174         // Fixed input.
175         $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12 - 14 '));
176         $this->assertTrue(address_in_subnet('bab:baba::babe', 'bab:baba::baba - babe  '));
178         // Incorrect input.
179         $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-234.14'));
180         $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-256'));
181         $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12--256'));
183         // 3: xxx.xxx or xxx.xxx. or xxx:xxx:xxxx or xxx:xxx:xxxx. (incomplete address, a bit non-technical ;-).
184         $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12'));
185         $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.23.13'));
186         $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.'));
187         $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234'));
188         $this->assertTrue(address_in_subnet('123.121.234.12', '123.121'));
189         $this->assertTrue(address_in_subnet('123.121.234.12', '123'));
190         $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234.'));
191         $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234'));
192         $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba::bab'));
193         $this->assertFalse(address_in_subnet('baba:baba::ba', 'baba:baba::bc'));
194         $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba'));
195         $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:'));
196         $this->assertFalse(address_in_subnet('bab:baba::bab', 'baba:'));
198         // Multiple subnets.
199         $this->assertTrue(address_in_subnet('123.121.234.12', '::1/64, 124., 123.121.234.10-30'));
200         $this->assertTrue(address_in_subnet('124.121.234.12', '::1/64, 124., 123.121.234.10-30'));
201         $this->assertTrue(address_in_subnet('::2',            '::1/64, 124., 123.121.234.10-30'));
202         $this->assertFalse(address_in_subnet('12.121.234.12', '::1/64, 124., 123.121.234.10-30'));
204         // Other incorrect input.
205         $this->assertFalse(address_in_subnet('123.123.123.123', ''));
206     }
208     /**
209      * Modifies $_SERVER['HTTP_USER_AGENT'] manually to check if check_browser_version
210      * works as expected.
211      */
212     public function test_check_browser_version() {
213         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Safari']['412']['Mac OS X'];
214         $this->assertTrue(check_browser_version('Safari'));
215         $this->assertTrue(check_browser_version('WebKit'));
216         $this->assertTrue(check_browser_version('Safari', '312'));
217         $this->assertFalse(check_browser_version('Safari', '500'));
218         $this->assertFalse(check_browser_version('Chrome'));
219         $this->assertFalse(check_browser_version('Safari iOS'));
221         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Safari iOS']['528']['iPhone'];
222         $this->assertTrue(check_browser_version('Safari iOS'));
223         $this->assertTrue(check_browser_version('WebKit'));
224         $this->assertTrue(check_browser_version('Safari iOS', '527'));
225         $this->assertFalse(check_browser_version('Safari iOS', 590));
226         $this->assertFalse(check_browser_version('Safari', '312'));
227         $this->assertFalse(check_browser_version('Safari', '500'));
228         $this->assertFalse(check_browser_version('Chrome'));
230         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['WebKit Android']['530']['Nexus'];
231         $this->assertTrue(check_browser_version('WebKit'));
232         $this->assertTrue(check_browser_version('WebKit Android', '527'));
233         $this->assertFalse(check_browser_version('WebKit Android', 590));
234         $this->assertFalse(check_browser_version('Safari'));
235         $this->assertFalse(check_browser_version('Chrome'));
237         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Chrome']['8']['Mac OS X'];
238         $this->assertTrue(check_browser_version('Chrome'));
239         $this->assertTrue(check_browser_version('WebKit'));
240         $this->assertTrue(check_browser_version('Chrome', 8));
241         $this->assertFalse(check_browser_version('Chrome', 10));
242         $this->assertFalse(check_browser_version('Safari', '1'));
244         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Opera']['9.0']['Windows XP'];
245         $this->assertTrue(check_browser_version('Opera'));
246         $this->assertTrue(check_browser_version('Opera', '8.0'));
247         $this->assertFalse(check_browser_version('Opera', '10.0'));
249         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['6.0']['Windows XP SP2'];
250         $this->assertTrue(check_browser_version('MSIE'));
251         $this->assertTrue(check_browser_version('MSIE', '5.0'));
252         $this->assertFalse(check_browser_version('MSIE', '7.0'));
254         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['5.0']['Windows 98'];
255         $this->assertFalse(check_browser_version('MSIE'));
256         $this->assertTrue(check_browser_version('MSIE', 0));
257         $this->assertTrue(check_browser_version('MSIE', '5.0'));
258         $this->assertFalse(check_browser_version('MSIE', '7.0'));
260         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['9.0']['Windows 7'];
261         $this->assertTrue(check_browser_version('MSIE'));
262         $this->assertTrue(check_browser_version('MSIE', 0));
263         $this->assertTrue(check_browser_version('MSIE', '5.0'));
264         $this->assertTrue(check_browser_version('MSIE', '9.0'));
265         $this->assertFalse(check_browser_version('MSIE', '10'));
267         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['9.0i']['Windows 7'];
268         $this->assertTrue(check_browser_version('MSIE'));
269         $this->assertTrue(check_browser_version('MSIE', 0));
270         $this->assertTrue(check_browser_version('MSIE', '5.0'));
271         $this->assertTrue(check_browser_version('MSIE', '9.0'));
272         $this->assertFalse(check_browser_version('MSIE', '10'));
274         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['10.0']['Windows 8'];
275         $this->assertTrue(check_browser_version('MSIE'));
276         $this->assertTrue(check_browser_version('MSIE', 0));
277         $this->assertTrue(check_browser_version('MSIE', '5.0'));
278         $this->assertTrue(check_browser_version('MSIE', '9.0'));
279         $this->assertTrue(check_browser_version('MSIE', '10'));
280         $this->assertFalse(check_browser_version('MSIE', '11'));
282         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['10.0i']['Windows 8'];
283         $this->assertTrue(check_browser_version('MSIE'));
284         $this->assertTrue(check_browser_version('MSIE', 0));
285         $this->assertTrue(check_browser_version('MSIE', '5.0'));
286         $this->assertTrue(check_browser_version('MSIE', '9.0'));
287         $this->assertTrue(check_browser_version('MSIE', '10'));
288         $this->assertFalse(check_browser_version('MSIE', '11'));
290         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['2.0']['Windows XP'];
291         $this->assertTrue(check_browser_version('Firefox'));
292         $this->assertTrue(check_browser_version('Firefox', '1.5'));
293         $this->assertFalse(check_browser_version('Firefox', '3.0'));
294         $this->assertTrue(check_browser_version('Gecko', '2'));
295         $this->assertTrue(check_browser_version('Gecko', 20030516));
296         $this->assertTrue(check_browser_version('Gecko', 20051106));
297         $this->assertTrue(check_browser_version('Gecko', 2006010100));
299         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['1.0.6']['Windows XP'];
300         $this->assertTrue(check_browser_version('Firefox'));
301         $this->assertTrue(check_browser_version('Gecko', '1'));
302         $this->assertFalse(check_browser_version('Gecko', 20030516));
303         $this->assertFalse(check_browser_version('Gecko', 20051106));
304         $this->assertFalse(check_browser_version('Gecko', 2006010100));
305         $this->assertFalse(check_browser_version('Firefox', '1.5'));
306         $this->assertFalse(check_browser_version('Firefox', '3.0'));
307         $this->assertFalse(check_browser_version('Gecko', '2'));
309         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['2.0']['Windows XP'];
310         $this->assertTrue(check_browser_version('Firefox'));
311         $this->assertTrue(check_browser_version('Firefox', '1.5'));
312         $this->assertTrue(check_browser_version('Gecko', '1'));
313         $this->assertTrue(check_browser_version('Gecko', '2'));
314         $this->assertTrue(check_browser_version('Gecko', 20030516));
315         $this->assertTrue(check_browser_version('Gecko', 20051106));
316         $this->assertTrue(check_browser_version('Gecko', 2006010100));
317         $this->assertFalse(check_browser_version('Firefox', '3.0'));
319         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['3.6']['Linux'];
320         $this->assertTrue(check_browser_version('Firefox'));
321         $this->assertTrue(check_browser_version('Firefox', '1.5'));
322         $this->assertTrue(check_browser_version('Firefox', '3.0'));
323         $this->assertTrue(check_browser_version('Gecko', '2'));
324         $this->assertTrue(check_browser_version('Gecko', '3.6'));
325         $this->assertTrue(check_browser_version('Gecko', 20030516));
326         $this->assertTrue(check_browser_version('Gecko', 20051106));
327         $this->assertTrue(check_browser_version('Gecko', 2006010100));
328         $this->assertFalse(check_browser_version('Firefox', '4'));
329         $this->assertFalse(check_browser_version('Firefox', '10'));
331         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['3.6']['Linux'];
332         $this->assertTrue(check_browser_version('Firefox'));
333         $this->assertTrue(check_browser_version('Firefox', '1.5'));
334         $this->assertTrue(check_browser_version('Firefox', '3.0'));
335         $this->assertTrue(check_browser_version('Gecko', '2'));
336         $this->assertTrue(check_browser_version('Gecko', '3.6'));
337         $this->assertTrue(check_browser_version('Gecko', 20030516));
338         $this->assertTrue(check_browser_version('Gecko', 20051106));
339         $this->assertTrue(check_browser_version('Gecko', 2006010100));
340         $this->assertFalse(check_browser_version('Firefox', '4'));
341         $this->assertFalse(check_browser_version('Firefox', '10'));
342         $this->assertFalse(check_browser_version('Firefox', '18'));
343         $this->assertFalse(check_browser_version('Gecko', '4'));
345         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['15.0a2']['Windows'];
346         $this->assertTrue(check_browser_version('Firefox'));
347         $this->assertTrue(check_browser_version('Firefox', '1.5'));
348         $this->assertTrue(check_browser_version('Firefox', '3.0'));
349         $this->assertTrue(check_browser_version('Gecko', '2'));
350         $this->assertTrue(check_browser_version('Gecko', '3.6'));
351         $this->assertTrue(check_browser_version('Gecko', '15.0'));
352         $this->assertTrue(check_browser_version('Gecko', 20030516));
353         $this->assertTrue(check_browser_version('Gecko', 20051106));
354         $this->assertTrue(check_browser_version('Gecko', 2006010100));
355         $this->assertTrue(check_browser_version('Firefox', '4'));
356         $this->assertTrue(check_browser_version('Firefox', '10'));
357         $this->assertTrue(check_browser_version('Firefox', '15'));
358         $this->assertFalse(check_browser_version('Firefox', '18'));
359         $this->assertFalse(check_browser_version('Gecko', '18'));
361         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['18.0']['Mac OS X'];
362         $this->assertTrue(check_browser_version('Firefox'));
363         $this->assertTrue(check_browser_version('Firefox', '1.5'));
364         $this->assertTrue(check_browser_version('Firefox', '3.0'));
365         $this->assertTrue(check_browser_version('Gecko', '2'));
366         $this->assertTrue(check_browser_version('Gecko', '3.6'));
367         $this->assertTrue(check_browser_version('Gecko', '15.0'));
368         $this->assertTrue(check_browser_version('Gecko', '18.0'));
369         $this->assertTrue(check_browser_version('Gecko', 20030516));
370         $this->assertTrue(check_browser_version('Gecko', 20051106));
371         $this->assertTrue(check_browser_version('Gecko', 2006010100));
372         $this->assertTrue(check_browser_version('Firefox', '4'));
373         $this->assertTrue(check_browser_version('Firefox', '10'));
374         $this->assertTrue(check_browser_version('Firefox', '15'));
375         $this->assertTrue(check_browser_version('Firefox', '18'));
376         $this->assertFalse(check_browser_version('Firefox', '19'));
377         $this->assertFalse(check_browser_version('Gecko', '19'));
379         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['SeaMonkey']['2.0']['Windows'];
381         $this->assertTrue(check_browser_version('Gecko', '2'));
382         $this->assertTrue(check_browser_version('Gecko', 20030516));
383         $this->assertTrue(check_browser_version('Gecko', 20051106));
384         $this->assertTrue(check_browser_version('Gecko', 2006010100));
385         $this->assertFalse(check_browser_version('Gecko', '3.6'));
386         $this->assertFalse(check_browser_version('Gecko', '4.0'));
387         $this->assertFalse(check_browser_version('Firefox'));
389         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['SeaMonkey']['2.1']['Linux'];
390         $this->assertTrue(check_browser_version('Gecko', '2'));
391         $this->assertTrue(check_browser_version('Gecko', '3.6'));
392         $this->assertTrue(check_browser_version('Gecko', '4.0'));
393         $this->assertTrue(check_browser_version('Gecko', 20030516));
394         $this->assertTrue(check_browser_version('Gecko', 20051106));
395         $this->assertTrue(check_browser_version('Gecko', 2006010100));
396         $this->assertTrue(check_browser_version('Firefox'));
397         $this->assertTrue(check_browser_version('Firefox', 4.0));
398         $this->assertFalse(check_browser_version('Firefox', 5));
399         $this->assertFalse(check_browser_version('Gecko', '18.0'));
401     }
403     public function test_get_browser_version_classes() {
404         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Safari']['412']['Mac OS X'];
405         $this->assertEquals(array('safari'), get_browser_version_classes());
407         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Chrome']['8']['Mac OS X'];
408         $this->assertEquals(array('safari'), get_browser_version_classes());
410         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Safari iOS']['528']['iPhone'];
411         $this->assertEquals(array('safari', 'ios'), get_browser_version_classes());
413         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['WebKit Android']['530']['Nexus'];
414         $this->assertEquals(array('safari', 'android'), get_browser_version_classes());
416         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Chrome']['8']['Mac OS X'];
417         $this->assertEquals(array('safari'), get_browser_version_classes());
419         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Opera']['9.0']['Windows XP'];
420         $this->assertEquals(array('opera'), get_browser_version_classes());
422         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['6.0']['Windows XP SP2'];
423         $this->assertEquals(array('ie', 'ie6'), get_browser_version_classes());
425         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['7.0']['Windows XP SP2'];
426         $this->assertEquals(array('ie', 'ie7'), get_browser_version_classes());
428         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['8.0']['Windows Vista'];
429         $this->assertEquals(array('ie', 'ie8'), get_browser_version_classes());
431         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['9.0']['Windows 7'];
432         $this->assertEquals(array('ie', 'ie9'), get_browser_version_classes());
434         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['9.0i']['Windows 7'];
435         $this->assertEquals(array('ie', 'ie9'), get_browser_version_classes());
437         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['10.0']['Windows 8'];
438         $this->assertEquals(array('ie', 'ie10'), get_browser_version_classes());
440         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['MSIE']['10.0i']['Windows 8'];
441         $this->assertEquals(array('ie', 'ie10'), get_browser_version_classes());
443         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['2.0']['Windows XP'];
444         $this->assertEquals(array('gecko', 'gecko18'), get_browser_version_classes());
446         $_SERVER['HTTP_USER_AGENT'] = $this->user_agents['Firefox']['3.0.6']['SUSE'];
447         $this->assertEquals(array('gecko', 'gecko19'), get_browser_version_classes());
448     }
450     public function test_get_device_type() {
451         // IE8 (common pattern ~1.5% of IE7/8 users have embedded IE6 agent).
452         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; BT Openworld BB; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; Hotbar 10.2.197.0; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 2.0.50727)';
453         $this->assertEquals('default', get_device_type());
454         // Genuine IE6.
455         $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/4.0 (compatible; MSIE 6.0; AOL 9.0; Windows NT 5.1; SV1; FunWebProducts; .NET CLR 1.0.3705; Media Center PC 2.8)';
456         $this->assertEquals('legacy', get_device_type());
457     }
459     public function test_fix_utf8() {
460         // Make sure valid data including other types is not changed.
461         $this->assertSame(null, fix_utf8(null));
462         $this->assertSame(1, fix_utf8(1));
463         $this->assertSame(1.1, fix_utf8(1.1));
464         $this->assertSame(true, fix_utf8(true));
465         $this->assertSame('', fix_utf8(''));
466         $this->assertSame('abc', fix_utf8('abc'));
467         $array = array('do', 're', 'mi');
468         $this->assertSame($array, fix_utf8($array));
469         $object = new stdClass();
470         $object->a = 'aa';
471         $object->b = 'bb';
472         $this->assertEquals($object, fix_utf8($object));
474         // Valid utf8 string.
475         $this->assertSame("žlutý koníček přeskočil potůček \n\t\r\0", fix_utf8("žlutý koníček přeskočil potůček \n\t\r\0"));
477         // Invalid utf8 string.
478         $this->assertSame('aš', fix_utf8('a'.chr(130).'š'), 'This fails with buggy iconv() when mbstring extenstion is not available as fallback.');
479     }
481     public function test_optional_param() {
482         global $CFG;
484         $_POST['username'] = 'post_user';
485         $_GET['username'] = 'get_user';
486         $this->assertSame($_POST['username'], optional_param('username', 'default_user', PARAM_RAW));
488         unset($_POST['username']);
489         $this->assertSame($_GET['username'], optional_param('username', 'default_user', PARAM_RAW));
491         unset($_GET['username']);
492         $this->assertSame('default_user', optional_param('username', 'default_user', PARAM_RAW));
494         // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
495         $_POST['username'] = 'post_user';
496         try {
497             optional_param('username', 'default_user', null);
498             $this->fail('coding_exception expected');
499         } catch (moodle_exception $ex) {
500             $this->assertInstanceOf('coding_exception', $ex);
501         }
502         try {
503             @optional_param('username', 'default_user');
504             $this->fail('coding_exception expected');
505         } catch (moodle_exception $ex) {
506             $this->assertInstanceOf('coding_exception', $ex);
507         }
508         try {
509             @optional_param('username');
510             $this->fail('coding_exception expected');
511         } catch (moodle_exception $ex) {
512             $this->assertInstanceOf('coding_exception', $ex);
513         }
514         try {
515             optional_param('', 'default_user', PARAM_RAW);
516             $this->fail('coding_exception expected');
517         } catch (moodle_exception $ex) {
518             $this->assertInstanceOf('coding_exception', $ex);
519         }
521         // Make sure warning is displayed if array submitted - TODO: throw exception in Moodle 2.3.
522         $_POST['username'] = array('a'=>'a');
523         $this->assertSame($_POST['username'], optional_param('username', 'default_user', PARAM_RAW));
524         $this->assertDebuggingCalled();
525     }
527     public function test_optional_param_array() {
528         global $CFG;
530         $_POST['username'] = array('a'=>'post_user');
531         $_GET['username'] = array('a'=>'get_user');
532         $this->assertSame($_POST['username'], optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
534         unset($_POST['username']);
535         $this->assertSame($_GET['username'], optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
537         unset($_GET['username']);
538         $this->assertSame(array('a'=>'default_user'), optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
540         // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
541         $_POST['username'] = array('a'=>'post_user');
542         try {
543             optional_param_array('username', array('a'=>'default_user'), null);
544             $this->fail('coding_exception expected');
545         } catch (moodle_exception $ex) {
546             $this->assertInstanceOf('coding_exception', $ex);
547         }
548         try {
549             @optional_param_array('username', array('a'=>'default_user'));
550             $this->fail('coding_exception expected');
551         } catch (moodle_exception $ex) {
552             $this->assertInstanceOf('coding_exception', $ex);
553         }
554         try {
555             @optional_param_array('username');
556             $this->fail('coding_exception expected');
557         } catch (moodle_exception $ex) {
558             $this->assertInstanceOf('coding_exception', $ex);
559         }
560         try {
561             optional_param_array('', array('a'=>'default_user'), PARAM_RAW);
562             $this->fail('coding_exception expected');
563         } catch (moodle_exception $ex) {
564             $this->assertInstanceOf('coding_exception', $ex);
565         }
567         // Do not allow nested arrays.
568         try {
569             $_POST['username'] = array('a'=>array('b'=>'post_user'));
570             optional_param_array('username', array('a'=>'default_user'), PARAM_RAW);
571             $this->fail('coding_exception expected');
572         } catch (coding_exception $ex) {
573             $this->assertTrue(true);
574         }
576         // Do not allow non-arrays.
577         $_POST['username'] = 'post_user';
578         $this->assertSame(array('a'=>'default_user'), optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
579         $this->assertDebuggingCalled();
581         // Make sure array keys are sanitised.
582         $_POST['username'] = array('abc123_;-/*-+ '=>'arrggh', 'a1_-'=>'post_user');
583         $this->assertSame(array('a1_-'=>'post_user'), optional_param_array('username', array(), PARAM_RAW));
584         $this->assertDebuggingCalled();
585     }
587     public function test_required_param() {
588         $_POST['username'] = 'post_user';
589         $_GET['username'] = 'get_user';
590         $this->assertSame('post_user', required_param('username', PARAM_RAW));
592         unset($_POST['username']);
593         $this->assertSame('get_user', required_param('username', PARAM_RAW));
595         unset($_GET['username']);
596         try {
597             $this->assertSame('default_user', required_param('username', PARAM_RAW));
598             $this->fail('moodle_exception expected');
599         } catch (moodle_exception $ex) {
600             $this->assertInstanceOf('moodle_exception', $ex);
601         }
603         // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
604         $_POST['username'] = 'post_user';
605         try {
606             @required_param('username');
607             $this->fail('coding_exception expected');
608         } catch (moodle_exception $ex) {
609             $this->assertInstanceOf('coding_exception', $ex);
610         }
611         try {
612             required_param('username', '');
613             $this->fail('coding_exception expected');
614         } catch (moodle_exception $ex) {
615             $this->assertInstanceOf('coding_exception', $ex);
616         }
617         try {
618             required_param('', PARAM_RAW);
619             $this->fail('coding_exception expected');
620         } catch (moodle_exception $ex) {
621             $this->assertInstanceOf('coding_exception', $ex);
622         }
624         // Make sure warning is displayed if array submitted - TODO: throw exception in Moodle 2.3.
625         $_POST['username'] = array('a'=>'a');
626         $this->assertSame($_POST['username'], required_param('username', PARAM_RAW));
627         $this->assertDebuggingCalled();
628     }
630     public function test_required_param_array() {
631         global $CFG;
633         $_POST['username'] = array('a'=>'post_user');
634         $_GET['username'] = array('a'=>'get_user');
635         $this->assertSame($_POST['username'], required_param_array('username', PARAM_RAW));
637         unset($_POST['username']);
638         $this->assertSame($_GET['username'], required_param_array('username', PARAM_RAW));
640         // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
641         $_POST['username'] = array('a'=>'post_user');
642         try {
643             required_param_array('username', null);
644             $this->fail('coding_exception expected');
645         } catch (moodle_exception $ex) {
646             $this->assertInstanceOf('coding_exception', $ex);
647         }
648         try {
649             @required_param_array('username');
650             $this->fail('coding_exception expected');
651         } catch (moodle_exception $ex) {
652             $this->assertInstanceOf('coding_exception', $ex);
653         }
654         try {
655             required_param_array('', PARAM_RAW);
656             $this->fail('coding_exception expected');
657         } catch (moodle_exception $ex) {
658             $this->assertInstanceOf('coding_exception', $ex);
659         }
661         // Do not allow nested arrays.
662         try {
663             $_POST['username'] = array('a'=>array('b'=>'post_user'));
664             required_param_array('username', PARAM_RAW);
665             $this->fail('coding_exception expected');
666         } catch (moodle_exception $ex) {
667             $this->assertInstanceOf('coding_exception', $ex);
668         }
670         // Do not allow non-arrays.
671         try {
672             $_POST['username'] = 'post_user';
673             required_param_array('username', PARAM_RAW);
674             $this->fail('moodle_exception expected');
675         } catch (moodle_exception $ex) {
676             $this->assertInstanceOf('moodle_exception', $ex);
677         }
679         // Make sure array keys are sanitised.
680         $_POST['username'] = array('abc123_;-/*-+ '=>'arrggh', 'a1_-'=>'post_user');
681         $this->assertSame(array('a1_-'=>'post_user'), required_param_array('username', PARAM_RAW));
682         $this->assertDebuggingCalled();
683     }
685     public function test_clean_param() {
686         // Forbid objects and arrays.
687         try {
688             clean_param(array('x', 'y'), PARAM_RAW);
689             $this->fail('coding_exception expected');
690         } catch (moodle_exception $ex) {
691             $this->assertInstanceOf('coding_exception', $ex);
692         }
693         try {
694             $param = new stdClass();
695             $param->id = 1;
696             clean_param($param, PARAM_RAW);
697             $this->fail('coding_exception expected');
698         } catch (moodle_exception $ex) {
699             $this->assertInstanceOf('coding_exception', $ex);
700         }
702         // Require correct type.
703         try {
704             clean_param('x', 'xxxxxx');
705             $this->fail('moodle_exception expected');
706         } catch (moodle_exception $ex) {
707             $this->assertInstanceOf('moodle_exception', $ex);
708         }
709         try {
710             @clean_param('x');
711             $this->fail('moodle_exception expected');
712         } catch (moodle_exception $ex) {
713             $this->assertInstanceOf('moodle_exception', $ex);
714         }
715     }
717     public function test_clean_param_array() {
718         $this->assertSame(array(), clean_param_array(null, PARAM_RAW));
719         $this->assertSame(array('a', 'b'), clean_param_array(array('a', 'b'), PARAM_RAW));
720         $this->assertSame(array('a', array('b')), clean_param_array(array('a', array('b')), PARAM_RAW, true));
722         // Require correct type.
723         try {
724             clean_param_array(array('x'), 'xxxxxx');
725             $this->fail('moodle_exception expected');
726         } catch (moodle_exception $ex) {
727             $this->assertInstanceOf('moodle_exception', $ex);
728         }
729         try {
730             @clean_param_array(array('x'));
731             $this->fail('moodle_exception expected');
732         } catch (moodle_exception $ex) {
733             $this->assertInstanceOf('moodle_exception', $ex);
734         }
736         try {
737             clean_param_array(array('x', array('y')), PARAM_RAW);
738             $this->fail('coding_exception expected');
739         } catch (moodle_exception $ex) {
740             $this->assertInstanceOf('coding_exception', $ex);
741         }
743         // Test recursive.
744     }
746     public function test_clean_param_raw() {
747         $this->assertSame(
748             '#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)',
749             clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_RAW));
750     }
752     public function test_clean_param_trim() {
753         $this->assertSame('Frog toad', clean_param("   Frog toad   \r\n  ", PARAM_RAW_TRIMMED));
754     }
756     public function test_clean_param_clean() {
757         // PARAM_CLEAN is an ugly hack, do not use in new code (skodak),
758         // instead use more specific type, or submit sothing that can be verified properly.
759         $this->assertSame('xx', clean_param('xx<script>', PARAM_CLEAN));
760     }
762     public function test_clean_param_alpha() {
763         $this->assertSame('DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHA));
764     }
766     public function test_clean_param_alphanum() {
767         $this->assertSame('978942897DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHANUM));
768     }
770     public function test_clean_param_alphaext() {
771         $this->assertSame('DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHAEXT));
772     }
774     public function test_clean_param_sequence() {
775         $this->assertSame(',9789,42897', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_SEQUENCE));
776     }
778     public function test_clean_param_component() {
779         // Please note the cleaning of component names is very strict, no guessing here.
780         $this->assertSame('mod_forum', clean_param('mod_forum', PARAM_COMPONENT));
781         $this->assertSame('block_online_users', clean_param('block_online_users', PARAM_COMPONENT));
782         $this->assertSame('block_blond_online_users', clean_param('block_blond_online_users', PARAM_COMPONENT));
783         $this->assertSame('mod_something2', clean_param('mod_something2', PARAM_COMPONENT));
784         $this->assertSame('forum', clean_param('forum', PARAM_COMPONENT));
785         $this->assertSame('user', clean_param('user', PARAM_COMPONENT));
786         $this->assertSame('rating', clean_param('rating', PARAM_COMPONENT));
787         $this->assertSame('', clean_param('mod_2something', PARAM_COMPONENT));
788         $this->assertSame('', clean_param('2mod_something', PARAM_COMPONENT));
789         $this->assertSame('', clean_param('mod_something_xx', PARAM_COMPONENT));
790         $this->assertSame('', clean_param('auth_something__xx', PARAM_COMPONENT));
791         $this->assertSame('', clean_param('mod_Something', PARAM_COMPONENT));
792         $this->assertSame('', clean_param('mod_somethíng', PARAM_COMPONENT));
793         $this->assertSame('', clean_param('auth_xx-yy', PARAM_COMPONENT));
794         $this->assertSame('', clean_param('_auth_xx', PARAM_COMPONENT));
795         $this->assertSame('', clean_param('a2uth_xx', PARAM_COMPONENT));
796         $this->assertSame('', clean_param('auth_xx_', PARAM_COMPONENT));
797         $this->assertSame('', clean_param('auth_xx.old', PARAM_COMPONENT));
798         $this->assertSame('', clean_param('_user', PARAM_COMPONENT));
799         $this->assertSame('', clean_param('2rating', PARAM_COMPONENT));
800         $this->assertSame('', clean_param('user_', PARAM_COMPONENT));
801     }
803     public function test_is_valid_plugin_name() {
804         $this->assertTrue(is_valid_plugin_name('forum'));
805         $this->assertTrue(is_valid_plugin_name('forum2'));
806         $this->assertTrue(is_valid_plugin_name('online_users'));
807         $this->assertTrue(is_valid_plugin_name('blond_online_users'));
808         $this->assertFalse(is_valid_plugin_name('online__users'));
809         $this->assertFalse(is_valid_plugin_name('forum '));
810         $this->assertFalse(is_valid_plugin_name('forum.old'));
811         $this->assertFalse(is_valid_plugin_name('xx-yy'));
812         $this->assertFalse(is_valid_plugin_name('2xx'));
813         $this->assertFalse(is_valid_plugin_name('Xx'));
814         $this->assertFalse(is_valid_plugin_name('_xx'));
815         $this->assertFalse(is_valid_plugin_name('xx_'));
816     }
818     public function test_clean_param_plugin() {
819         // Please note the cleaning of plugin names is very strict, no guessing here.
820         $this->assertSame('forum', clean_param('forum', PARAM_PLUGIN));
821         $this->assertSame('forum2', clean_param('forum2', PARAM_PLUGIN));
822         $this->assertSame('online_users', clean_param('online_users', PARAM_PLUGIN));
823         $this->assertSame('blond_online_users', clean_param('blond_online_users', PARAM_PLUGIN));
824         $this->assertSame('', clean_param('online__users', PARAM_PLUGIN));
825         $this->assertSame('', clean_param('forum ', PARAM_PLUGIN));
826         $this->assertSame('', clean_param('forum.old', PARAM_PLUGIN));
827         $this->assertSame('', clean_param('xx-yy', PARAM_PLUGIN));
828         $this->assertSame('', clean_param('2xx', PARAM_PLUGIN));
829         $this->assertSame('', clean_param('Xx', PARAM_PLUGIN));
830         $this->assertSame('', clean_param('_xx', PARAM_PLUGIN));
831         $this->assertSame('', clean_param('xx_', PARAM_PLUGIN));
832     }
834     public function test_clean_param_area() {
835         // Please note the cleaning of area names is very strict, no guessing here.
836         $this->assertSame('something', clean_param('something', PARAM_AREA));
837         $this->assertSame('something2', clean_param('something2', PARAM_AREA));
838         $this->assertSame('some_thing', clean_param('some_thing', PARAM_AREA));
839         $this->assertSame('some_thing_xx', clean_param('some_thing_xx', PARAM_AREA));
840         $this->assertSame('', clean_param('_something', PARAM_AREA));
841         $this->assertSame('', clean_param('something_', PARAM_AREA));
842         $this->assertSame('', clean_param('2something', PARAM_AREA));
843         $this->assertSame('', clean_param('Something', PARAM_AREA));
844         $this->assertSame('', clean_param('some-thing', PARAM_AREA));
845         $this->assertSame('', clean_param('somethííng', PARAM_AREA));
846         $this->assertSame('', clean_param('something.x', PARAM_AREA));
847     }
849     public function test_clean_param_text() {
850         $this->assertSame(PARAM_TEXT, PARAM_MULTILANG);
851         // Standard.
852         $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));
853         $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));
854         $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));
855         // Malformed.
856         $this->assertSame('<span lang="en" class="multilang">aa</span>', clean_param('<span lang="en" class="multilang">aa</span>', PARAM_TEXT));
857         $this->assertSame('aa', clean_param('<span lang="en" class="nothing" class="multilang">aa</span>', PARAM_TEXT));
858         $this->assertSame('aa', clean_param('<lang lang="en" class="multilang">aa</lang>', PARAM_TEXT));
859         $this->assertSame('aa', clean_param('<lang lang="en!!">aa</lang>', PARAM_TEXT));
860         $this->assertSame('aa', clean_param('<span lang="en==" class="multilang">aa</span>', PARAM_TEXT));
861         $this->assertSame('abc', clean_param('a<em>b</em>c', PARAM_TEXT));
862         $this->assertSame('a>c>', clean_param('a><xx >c>', PARAM_TEXT)); // Standard strip_tags() behaviour.
863         $this->assertSame('a', clean_param('a<b', PARAM_TEXT));
864         $this->assertSame('a>b', clean_param('a>b', PARAM_TEXT));
865         $this->assertSame('<lang lang="en">a>a</lang>', clean_param('<lang lang="en">a>a</lang>', PARAM_TEXT)); // Standard strip_tags() behaviour.
866         $this->assertSame('a', clean_param('<lang lang="en">a<a</lang>', PARAM_TEXT));
867         $this->assertSame('<lang lang="en">aa</lang>', clean_param('<lang lang="en">a<br>a</lang>', PARAM_TEXT));
868     }
870     public function test_clean_param_url() {
871         // Test PARAM_URL and PARAM_LOCALURL a bit.
872         $this->assertSame('http://google.com/', clean_param('http://google.com/', PARAM_URL));
873         $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));
874         $this->assertSame('http://localhost/', clean_param('http://localhost/', PARAM_URL));
875         $this->assertSame('http://0.255.1.1/numericip.php', clean_param('http://0.255.1.1/numericip.php', PARAM_URL));
876         $this->assertSame('/just/a/path', clean_param('/just/a/path', PARAM_URL));
877         $this->assertSame('', clean_param('funny:thing', PARAM_URL));
878     }
880     public function test_clean_param_localurl() {
881         global $CFG;
882         $this->assertSame('', clean_param('http://google.com/', PARAM_LOCALURL));
883         $this->assertSame('', clean_param('http://some.very.long.and.silly.domain/with/a/path/', PARAM_LOCALURL));
884         $this->assertSame(clean_param($CFG->wwwroot, PARAM_LOCALURL), $CFG->wwwroot);
885         $this->assertSame('/just/a/path', clean_param('/just/a/path', PARAM_LOCALURL));
886         $this->assertSame('', clean_param('funny:thing', PARAM_LOCALURL));
887         $this->assertSame('course/view.php?id=3', clean_param('course/view.php?id=3', PARAM_LOCALURL));
888     }
890     public function test_clean_param_file() {
891         $this->assertSame('correctfile.txt', clean_param('correctfile.txt', PARAM_FILE));
892         $this->assertSame('badfile.txt', clean_param('b\'a<d`\\/fi:l>e.t"x|t', PARAM_FILE));
893         $this->assertSame('..parentdirfile.txt', clean_param('../parentdirfile.txt', PARAM_FILE));
894         $this->assertSame('....grandparentdirfile.txt', clean_param('../../grandparentdirfile.txt', PARAM_FILE));
895         $this->assertSame('..winparentdirfile.txt', clean_param('..\winparentdirfile.txt', PARAM_FILE));
896         $this->assertSame('....wingrandparentdir.txt', clean_param('..\..\wingrandparentdir.txt', PARAM_FILE));
897         $this->assertSame('myfile.a.b.txt', clean_param('myfile.a.b.txt', PARAM_FILE));
898         $this->assertSame('myfile..a..b.txt', clean_param('myfile..a..b.txt', PARAM_FILE));
899         $this->assertSame('myfile.a..b...txt', clean_param('myfile.a..b...txt', PARAM_FILE));
900         $this->assertSame('myfile.a.txt', clean_param('myfile.a.txt', PARAM_FILE));
901         $this->assertSame('myfile...txt', clean_param('myfile...txt', PARAM_FILE));
902         $this->assertSame('...jpg', clean_param('...jpg', PARAM_FILE));
903         $this->assertSame('.a.b.', clean_param('.a.b.', PARAM_FILE));
904         $this->assertSame('', clean_param('.', PARAM_FILE));
905         $this->assertSame('', clean_param('..', PARAM_FILE));
906         $this->assertSame('...', clean_param('...', PARAM_FILE));
907         $this->assertSame('. . . .', clean_param('. . . .', PARAM_FILE));
908         $this->assertSame('dontrtrim.me. .. .. . ', clean_param('dontrtrim.me. .. .. . ', PARAM_FILE));
909         $this->assertSame(' . .dontltrim.me', clean_param(' . .dontltrim.me', PARAM_FILE));
910         $this->assertSame('here is a tab.txt', clean_param("here is a tab\t.txt", PARAM_FILE));
911         $this->assertSame('here is a linebreak.txt', clean_param("here is a line\r\nbreak.txt", PARAM_FILE));
913         // The following behaviours have been maintained although they seem a little odd.
914         $this->assertSame('funnything', clean_param('funny:thing', PARAM_FILE));
915         $this->assertSame('.currentdirfile.txt', clean_param('./currentdirfile.txt', PARAM_FILE));
916         $this->assertSame('ctempwindowsfile.txt', clean_param('c:\temp\windowsfile.txt', PARAM_FILE));
917         $this->assertSame('homeuserlinuxfile.txt', clean_param('/home/user/linuxfile.txt', PARAM_FILE));
918         $this->assertSame('~myfile.txt', clean_param('~/myfile.txt', PARAM_FILE));
919     }
921     public function test_clean_param_path() {
922         $this->assertSame('correctfile.txt', clean_param('correctfile.txt', PARAM_PATH));
923         $this->assertSame('bad/file.txt', clean_param('b\'a<d`\\/fi:l>e.t"x|t', PARAM_PATH));
924         $this->assertSame('/parentdirfile.txt', clean_param('../parentdirfile.txt', PARAM_PATH));
925         $this->assertSame('/grandparentdirfile.txt', clean_param('../../grandparentdirfile.txt', PARAM_PATH));
926         $this->assertSame('/winparentdirfile.txt', clean_param('..\winparentdirfile.txt', PARAM_PATH));
927         $this->assertSame('/wingrandparentdir.txt', clean_param('..\..\wingrandparentdir.txt', PARAM_PATH));
928         $this->assertSame('funnything', clean_param('funny:thing', PARAM_PATH));
929         $this->assertSame('./here', clean_param('./././here', PARAM_PATH));
930         $this->assertSame('./currentdirfile.txt', clean_param('./currentdirfile.txt', PARAM_PATH));
931         $this->assertSame('c/temp/windowsfile.txt', clean_param('c:\temp\windowsfile.txt', PARAM_PATH));
932         $this->assertSame('/home/user/linuxfile.txt', clean_param('/home/user/linuxfile.txt', PARAM_PATH));
933         $this->assertSame('/home../user ./.linuxfile.txt', clean_param('/home../user ./.linuxfile.txt', PARAM_PATH));
934         $this->assertSame('~/myfile.txt', clean_param('~/myfile.txt', PARAM_PATH));
935         $this->assertSame('~/myfile.txt', clean_param('~/../myfile.txt', PARAM_PATH));
936         $this->assertSame('/..b../.../myfile.txt', clean_param('/..b../.../myfile.txt', PARAM_PATH));
937         $this->assertSame('..b../.../myfile.txt', clean_param('..b../.../myfile.txt', PARAM_PATH));
938         $this->assertSame('/super/slashes/', clean_param('/super//slashes///', PARAM_PATH));
939     }
941     public function test_clean_param_username() {
942         global $CFG;
943         $currentstatus =  $CFG->extendedusernamechars;
945         // Run tests with extended character == false;.
946         $CFG->extendedusernamechars = false;
947         $this->assertSame('johndoe123', clean_param('johndoe123', PARAM_USERNAME) );
948         $this->assertSame('john.doe', clean_param('john.doe', PARAM_USERNAME));
949         $this->assertSame('john-doe', clean_param('john-doe', PARAM_USERNAME));
950         $this->assertSame('john-doe', clean_param('john- doe', PARAM_USERNAME));
951         $this->assertSame('john_doe', clean_param('john_doe', PARAM_USERNAME));
952         $this->assertSame('john@doe', clean_param('john@doe', PARAM_USERNAME));
953         $this->assertSame('johndoe', clean_param('john~doe', PARAM_USERNAME));
954         $this->assertSame('johndoe', clean_param('john´doe', PARAM_USERNAME));
955         $this->assertSame(clean_param('john#$%&() ', PARAM_USERNAME), 'john');
956         $this->assertSame('johnd', clean_param('JOHNdóé ', PARAM_USERNAME));
957         $this->assertSame(clean_param('john.,:;-_/|\ñÑ[]A_X-,D {} ~!@#$%^&*()_+ ?><[] ščřžžý ?ýá\9e?\9eý??\9adoe ', PARAM_USERNAME), 'john.-_a_x-d@_doe');
959         // Test success condition, if extendedusernamechars == ENABLE;.
960         $CFG->extendedusernamechars = true;
961         $this->assertSame('john_doe', clean_param('john_doe', PARAM_USERNAME));
962         $this->assertSame('john@doe', clean_param('john@doe', PARAM_USERNAME));
963         $this->assertSame(clean_param('john# $%&()+_^', PARAM_USERNAME), 'john#$%&()+_^');
964         $this->assertSame('john~doe', clean_param('john~doe', PARAM_USERNAME));
965         $this->assertSame('john´doe', clean_param('joHN´doe', PARAM_USERNAME));
966         $this->assertSame('johndoe', clean_param('johnDOE', PARAM_USERNAME));
967         $this->assertSame('johndóé', clean_param('johndóé ', PARAM_USERNAME));
969         $CFG->extendedusernamechars = $currentstatus;
970     }
972     public function test_clean_param_stringid() {
973         // Test string identifiers validation.
974         // Valid strings.
975         $this->assertSame('validstring', clean_param('validstring', PARAM_STRINGID));
976         $this->assertSame('mod/foobar:valid_capability', clean_param('mod/foobar:valid_capability', PARAM_STRINGID));
977         $this->assertSame('CZ', clean_param('CZ', PARAM_STRINGID));
978         $this->assertSame('application/vnd.ms-powerpoint', clean_param('application/vnd.ms-powerpoint', PARAM_STRINGID));
979         $this->assertSame('grade2', clean_param('grade2', PARAM_STRINGID));
980         // Invalid strings.
981         $this->assertSame('', clean_param('trailing ', PARAM_STRINGID));
982         $this->assertSame('', clean_param('space bar', PARAM_STRINGID));
983         $this->assertSame('', clean_param('0numeric', PARAM_STRINGID));
984         $this->assertSame('', clean_param('*', PARAM_STRINGID));
985         $this->assertSame('', clean_param(' ', PARAM_STRINGID));
986     }
988     public function test_clean_param_timezone() {
989         // Test timezone validation.
990         $testvalues = array (
991             'America/Jamaica'                => 'America/Jamaica',
992             'America/Argentina/Cordoba'      => 'America/Argentina/Cordoba',
993             'America/Port-au-Prince'         => 'America/Port-au-Prince',
994             'America/Argentina/Buenos_Aires' => 'America/Argentina/Buenos_Aires',
995             'PST8PDT'                        => 'PST8PDT',
996             'Wrong.Value'                    => '',
997             'Wrong/.Value'                   => '',
998             'Wrong(Value)'                   => '',
999             '0'                              => '0',
1000             '0.0'                            => '0.0',
1001             '0.5'                            => '0.5',
1002             '9.0'                            => '9.0',
1003             '-9.0'                           => '-9.0',
1004             '+9.0'                           => '+9.0',
1005             '9.5'                            => '9.5',
1006             '-9.5'                           => '-9.5',
1007             '+9.5'                           => '+9.5',
1008             '12.0'                           => '12.0',
1009             '-12.0'                          => '-12.0',
1010             '+12.0'                          => '+12.0',
1011             '12.5'                           => '12.5',
1012             '-12.5'                          => '-12.5',
1013             '+12.5'                          => '+12.5',
1014             '13.0'                           => '13.0',
1015             '-13.0'                          => '-13.0',
1016             '+13.0'                          => '+13.0',
1017             '13.5'                           => '',
1018             '+13.5'                          => '',
1019             '-13.5'                          => '',
1020             '0.2'                            => '');
1022         foreach ($testvalues as $testvalue => $expectedvalue) {
1023             $actualvalue = clean_param($testvalue, PARAM_TIMEZONE);
1024             $this->assertEquals($expectedvalue, $actualvalue);
1025         }
1026     }
1028     public function test_validate_param() {
1029         try {
1030             $param = validate_param('11a', PARAM_INT);
1031             $this->fail('invalid_parameter_exception expected');
1032         } catch (moodle_exception $ex) {
1033             $this->assertInstanceOf('invalid_parameter_exception', $ex);
1034         }
1036         $param = validate_param('11', PARAM_INT);
1037         $this->assertSame(11, $param);
1039         try {
1040             $param = validate_param(null, PARAM_INT, false);
1041             $this->fail('invalid_parameter_exception expected');
1042         } catch (moodle_exception $ex) {
1043             $this->assertInstanceOf('invalid_parameter_exception', $ex);
1044         }
1046         $param = validate_param(null, PARAM_INT, true);
1047         $this->assertSame(null, $param);
1049         try {
1050             $param = validate_param(array(), PARAM_INT);
1051             $this->fail('invalid_parameter_exception expected');
1052         } catch (moodle_exception $ex) {
1053             $this->assertInstanceOf('invalid_parameter_exception', $ex);
1054         }
1055         try {
1056             $param = validate_param(new stdClass, PARAM_INT);
1057             $this->fail('invalid_parameter_exception expected');
1058         } catch (moodle_exception $ex) {
1059             $this->assertInstanceOf('invalid_parameter_exception', $ex);
1060         }
1062         $param = validate_param('1.0', PARAM_FLOAT);
1063         $this->assertSame(1.0, $param);
1065         // Make sure valid floats do not cause exception.
1066         validate_param(1.0, PARAM_FLOAT);
1067         validate_param(10, PARAM_FLOAT);
1068         validate_param('0', PARAM_FLOAT);
1069         validate_param('119813454.545464564564546564545646556564465465456465465465645645465645645645', PARAM_FLOAT);
1070         validate_param('011.1', PARAM_FLOAT);
1071         validate_param('11', PARAM_FLOAT);
1072         validate_param('+.1', PARAM_FLOAT);
1073         validate_param('-.1', PARAM_FLOAT);
1074         validate_param('1e10', PARAM_FLOAT);
1075         validate_param('.1e+10', PARAM_FLOAT);
1076         validate_param('1E-1', PARAM_FLOAT);
1078         try {
1079             $param = validate_param('1,2', PARAM_FLOAT);
1080             $this->fail('invalid_parameter_exception expected');
1081         } catch (moodle_exception $ex) {
1082             $this->assertInstanceOf('invalid_parameter_exception', $ex);
1083         }
1084         try {
1085             $param = validate_param('', PARAM_FLOAT);
1086             $this->fail('invalid_parameter_exception expected');
1087         } catch (moodle_exception $ex) {
1088             $this->assertInstanceOf('invalid_parameter_exception', $ex);
1089         }
1090         try {
1091             $param = validate_param('.', PARAM_FLOAT);
1092             $this->fail('invalid_parameter_exception expected');
1093         } catch (moodle_exception $ex) {
1094             $this->assertInstanceOf('invalid_parameter_exception', $ex);
1095         }
1096         try {
1097             $param = validate_param('e10', PARAM_FLOAT);
1098             $this->fail('invalid_parameter_exception expected');
1099         } catch (moodle_exception $ex) {
1100             $this->assertInstanceOf('invalid_parameter_exception', $ex);
1101         }
1102         try {
1103             $param = validate_param('abc', PARAM_FLOAT);
1104             $this->fail('invalid_parameter_exception expected');
1105         } catch (moodle_exception $ex) {
1106             $this->assertInstanceOf('invalid_parameter_exception', $ex);
1107         }
1108     }
1110     public function test_shorten_text_no_tags_already_short_enough() {
1111         // ......12345678901234567890123456.
1112         $text = "short text already no tags";
1113         $this->assertSame($text, shorten_text($text));
1114     }
1116     public function test_shorten_text_with_tags_already_short_enough() {
1117         // .........123456...7890....12345678.......901234567.
1118         $text = "<p>short <b>text</b> already</p><p>with tags</p>";
1119         $this->assertSame($text, shorten_text($text));
1120     }
1122     public function test_shorten_text_no_tags_needs_shortening() {
1123         // Default truncation is after 30 chars, but allowing 3 for the final '...'.
1124         // ......12345678901234567890123456789023456789012345678901234.
1125         $text = "long text without any tags blah de blah blah blah what";
1126         $this->assertSame('long text without any tags ...', shorten_text($text));
1127     }
1129     public function test_shorten_text_with_tags_needs_shortening() {
1130         // .......................................123456789012345678901234567890...
1131         $text = "<div class='frog'><p><blockquote>Long text with tags that will ".
1132             "be chopped off but <b>should be added back again</b></blockquote></p></div>";
1133         $this->assertEquals("<div class='frog'><p><blockquote>Long text with " .
1134             "tags that ...</blockquote></p></div>", shorten_text($text));
1135     }
1137     public function test_shorten_text_with_entities() {
1138         // Remember to allow 3 chars for the final '...'.
1139         // ......123456789012345678901234567_____890...
1140         $text = "some text which shouldn't &nbsp; break there";
1141         $this->assertSame("some text which shouldn't &nbsp; ...", shorten_text($text, 31));
1142         $this->assertSame("some text which shouldn't &nbsp;...", shorten_text($text, 30));
1143         $this->assertSame("some text which shouldn't ...", shorten_text($text, 29));
1144     }
1146     public function test_shorten_text_known_tricky_case() {
1147         // This case caused a bug up to 1.9.5
1148         // ..........123456789012345678901234567890123456789.....0_____1___2___...
1149         $text = "<h3>standard 'break-out' sub groups in TGs?</h3>&nbsp;&lt;&lt;There are several";
1150         $this->assertSame("<h3>standard 'break-out' sub groups in ...</h3>",
1151             shorten_text($text, 41));
1152         $this->assertSame("<h3>standard 'break-out' sub groups in TGs?...</h3>",
1153             shorten_text($text, 42));
1154         $this->assertSame("<h3>standard 'break-out' sub groups in TGs?</h3>&nbsp;...",
1155             shorten_text($text, 43));
1156     }
1158     public function test_shorten_text_no_spaces() {
1159         // ..........123456789.
1160         $text = "<h1>123456789</h1>"; // A string with no convenient breaks.
1161         $this->assertSame("<h1>12345...</h1>", shorten_text($text, 8));
1162     }
1164     public function test_shorten_text_utf8_european() {
1165         // Text without tags.
1166         // ......123456789012345678901234567.
1167         $text = "Žluťoučký koníček přeskočil";
1168         $this->assertSame($text, shorten_text($text)); // 30 chars by default.
1169         $this->assertSame("Žluťoučký koníče...", shorten_text($text, 19, true));
1170         $this->assertSame("Žluťoučký ...", shorten_text($text, 19, false));
1171         // And try it with 2-less (that are, in bytes, the middle of a sequence).
1172         $this->assertSame("Žluťoučký koní...", shorten_text($text, 17, true));
1173         $this->assertSame("Žluťoučký ...", shorten_text($text, 17, false));
1175         // .........123456789012345678...901234567....89012345.
1176         $text = "<p>Žluťoučký koníček <b>přeskočil</b> potůček</p>";
1177         $this->assertSame($text, shorten_text($text, 60));
1178         $this->assertSame("<p>Žluťoučký koníček ...</p>", shorten_text($text, 21));
1179         $this->assertSame("<p>Žluťoučký koníče...</p>", shorten_text($text, 19, true));
1180         $this->assertSame("<p>Žluťoučký ...</p>", shorten_text($text, 19, false));
1181         // And try it with 2 fewer (that are, in bytes, the middle of a sequence).
1182         $this->assertSame("<p>Žluťoučký koní...</p>", shorten_text($text, 17, true));
1183         $this->assertSame("<p>Žluťoučký ...</p>", shorten_text($text, 17, false));
1184         // And try over one tag (start/end), it does proper text len.
1185         $this->assertSame("<p>Žluťoučký koníček <b>př...</b></p>", shorten_text($text, 23, true));
1186         $this->assertSame("<p>Žluťoučký koníček <b>přeskočil</b> pot...</p>", shorten_text($text, 34, true));
1187         // And in the middle of one tag.
1188         $this->assertSame("<p>Žluťoučký koníček <b>přeskočil...</b></p>", shorten_text($text, 30, true));
1189     }
1191     public function test_shorten_text_utf8_oriental() {
1192         // Japanese
1193         // text without tags
1194         // ......123456789012345678901234.
1195         $text = '言語設定言語設定abcdefghijkl';
1196         $this->assertSame($text, shorten_text($text)); // 30 chars by default.
1197         $this->assertSame("言語設定言語...", shorten_text($text, 9, true));
1198         $this->assertSame("言語設定言語...", shorten_text($text, 9, false));
1199         $this->assertSame("言語設定言語設定ab...", shorten_text($text, 13, true));
1200         $this->assertSame("言語設定言語設定...", shorten_text($text, 13, false));
1202         // Chinese
1203         // text without tags
1204         // ......123456789012345678901234.
1205         $text = '简体中文简体中文abcdefghijkl';
1206         $this->assertSame($text, shorten_text($text)); // 30 chars by default.
1207         $this->assertSame("简体中文简体...", shorten_text($text, 9, true));
1208         $this->assertSame("简体中文简体...", shorten_text($text, 9, false));
1209         $this->assertSame("简体中文简体中文ab...", shorten_text($text, 13, true));
1210         $this->assertSame("简体中文简体中文...", shorten_text($text, 13, false));
1211     }
1213     public function test_shorten_text_multilang() {
1214         // This is not necessaryily specific to multilang. The issue is really
1215         // tags with attributes, where before we were generating invalid HTML
1216         // output like shorten_text('<span id="x" class="y">A</span> B', 1)
1217         // returning '<span id="x" ...</span>'. It is just that multilang
1218         // requires the sort of HTML that is quite likely to trigger this.
1219         // ........................................1...
1220         $text = '<span lang="en" class="multilang">A</span>' .
1221                 '<span lang="fr" class="multilang">B</span>';
1222         $this->assertSame('<span lang="en" class="multilang">...</span>',
1223                 shorten_text($text, 1));
1224     }
1226     public function test_usergetdate() {
1227         global $USER, $CFG, $DB;
1228         $this->resetAfterTest();
1230         // Check if forcetimezone is set then save it and set it to use user timezone.
1231         $cfgforcetimezone = null;
1232         if (isset($CFG->forcetimezone)) {
1233             $cfgforcetimezone = $CFG->forcetimezone;
1234             $CFG->forcetimezone = 99; // Get user default timezone.
1235         }
1237         $this->setAdminUser();
1239         $userstimezone = $USER->timezone;
1240         $USER->timezone = 2;// Set the timezone to a known state.
1242         // The string version of date comes from server locale setting and does
1243         // not respect user language, so it is necessary to reset that.
1244         $oldlocale = setlocale(LC_TIME, '0');
1245         setlocale(LC_TIME, 'en_AU.UTF-8');
1247         $ts = 1261540267; // The time this function was created.
1249         $arr = usergetdate($ts, 1); // Specify the timezone as an argument.
1250         $arr = array_values($arr);
1252         list($seconds, $minutes, $hours, $mday, $wday, $mon, $year, $yday, $weekday, $month) = $arr;
1253         $this->assertSame(7, $seconds);
1254         $this->assertSame(51, $minutes);
1255         $this->assertSame(4, $hours);
1256         $this->assertSame(23, $mday);
1257         $this->assertSame(3, $wday);
1258         $this->assertSame(12, $mon);
1259         $this->assertSame(2009, $year);
1260         $this->assertSame(356, $yday);
1261         $this->assertSame('Wednesday', $weekday);
1262         $this->assertSame('December', $month);
1263         $arr = usergetdate($ts); // Gets the timezone from the $USER object.
1264         $arr = array_values($arr);
1266         list($seconds, $minutes, $hours, $mday, $wday, $mon, $year, $yday, $weekday, $month) = $arr;
1267         $this->assertSame(7, $seconds);
1268         $this->assertSame(51, $minutes);
1269         $this->assertSame(5, $hours);
1270         $this->assertSame(23, $mday);
1271         $this->assertSame(3, $wday);
1272         $this->assertSame(12, $mon);
1273         $this->assertSame(2009, $year);
1274         $this->assertSame(356, $yday);
1275         $this->assertSame('Wednesday', $weekday);
1276         $this->assertSame('December', $month);
1277         // Set the timezone back to what it was.
1278         $USER->timezone = $userstimezone;
1280         // Restore forcetimezone if changed.
1281         if (!is_null($cfgforcetimezone)) {
1282             $CFG->forcetimezone = $cfgforcetimezone;
1283         }
1285         setlocale(LC_TIME, $oldlocale);
1286     }
1288     public function test_mark_user_preferences_changed() {
1289         $this->resetAfterTest();
1290         $otheruser = $this->getDataGenerator()->create_user();
1291         $otheruserid = $otheruser->id;
1293         set_cache_flag('userpreferenceschanged', $otheruserid, null);
1294         mark_user_preferences_changed($otheruserid);
1296         $this->assertEquals(get_cache_flag('userpreferenceschanged', $otheruserid, time()-10), 1);
1297         set_cache_flag('userpreferenceschanged', $otheruserid, null);
1298     }
1300     public function test_check_user_preferences_loaded() {
1301         global $DB;
1302         $this->resetAfterTest();
1304         $otheruser = $this->getDataGenerator()->create_user();
1305         $otheruserid = $otheruser->id;
1307         $DB->delete_records('user_preferences', array('userid'=>$otheruserid));
1308         set_cache_flag('userpreferenceschanged', $otheruserid, null);
1310         $user = new stdClass();
1311         $user->id = $otheruserid;
1313         // Load.
1314         check_user_preferences_loaded($user);
1315         $this->assertTrue(isset($user->preference));
1316         $this->assertTrue(is_array($user->preference));
1317         $this->assertArrayHasKey('_lastloaded', $user->preference);
1318         $this->assertCount(1, $user->preference);
1320         // Add preference via direct call.
1321         $DB->insert_record('user_preferences', array('name'=>'xxx', 'value'=>'yyy', 'userid'=>$user->id));
1323         // No cache reload yet.
1324         check_user_preferences_loaded($user);
1325         $this->assertCount(1, $user->preference);
1327         // Forced reloading of cache.
1328         unset($user->preference);
1329         check_user_preferences_loaded($user);
1330         $this->assertCount(2, $user->preference);
1331         $this->assertSame('yyy', $user->preference['xxx']);
1333         // Add preference via direct call.
1334         $DB->insert_record('user_preferences', array('name'=>'aaa', 'value'=>'bbb', 'userid'=>$user->id));
1336         // Test timeouts and modifications from different session.
1337         set_cache_flag('userpreferenceschanged', $user->id, 1, time() + 1000);
1338         $user->preference['_lastloaded'] = $user->preference['_lastloaded'] - 20;
1339         check_user_preferences_loaded($user);
1340         $this->assertCount(2, $user->preference);
1341         check_user_preferences_loaded($user, 10);
1342         $this->assertCount(3, $user->preference);
1343         $this->assertSame('bbb', $user->preference['aaa']);
1344         set_cache_flag('userpreferenceschanged', $user->id, null);
1345     }
1347     public function test_set_user_preference() {
1348         global $DB, $USER;
1349         $this->resetAfterTest();
1351         $this->setAdminUser();
1353         $otheruser = $this->getDataGenerator()->create_user();
1354         $otheruserid = $otheruser->id;
1356         $DB->delete_records('user_preferences', array('userid'=>$otheruserid));
1357         set_cache_flag('userpreferenceschanged', $otheruserid, null);
1359         $user = new stdClass();
1360         $user->id = $otheruserid;
1362         set_user_preference('aaa', 'bbb', $otheruserid);
1363         $this->assertSame('bbb', $DB->get_field('user_preferences', 'value', array('userid'=>$otheruserid, 'name'=>'aaa')));
1364         $this->assertSame('bbb', get_user_preferences('aaa', null, $otheruserid));
1366         set_user_preference('xxx', 'yyy', $user);
1367         $this->assertSame('yyy', $DB->get_field('user_preferences', 'value', array('userid'=>$otheruserid, 'name'=>'xxx')));
1368         $this->assertSame('yyy', get_user_preferences('xxx', null, $otheruserid));
1369         $this->assertTrue(is_array($user->preference));
1370         $this->assertSame('bbb', $user->preference['aaa']);
1371         $this->assertSame('yyy', $user->preference['xxx']);
1373         set_user_preference('xxx', null, $user);
1374         $this->assertFalse($DB->get_field('user_preferences', 'value', array('userid'=>$otheruserid, 'name'=>'xxx')));
1375         $this->assertNull(get_user_preferences('xxx', null, $otheruserid));
1377         set_user_preference('ooo', true, $user);
1378         $prefs = get_user_preferences(null, null, $otheruserid);
1379         $this->assertSame($user->preference['aaa'], $prefs['aaa']);
1380         $this->assertSame($user->preference['ooo'], $prefs['ooo']);
1381         $this->assertSame('1', $prefs['ooo']);
1383         set_user_preference('null', 0, $user);
1384         $this->assertSame('0', get_user_preferences('null', null, $otheruserid));
1386         $this->assertSame('lala', get_user_preferences('undefined', 'lala', $otheruserid));
1388         $DB->delete_records('user_preferences', array('userid'=>$otheruserid));
1389         set_cache_flag('userpreferenceschanged', $otheruserid, null);
1391         // Test $USER default.
1392         set_user_preference('_test_user_preferences_pref', 'ok');
1393         $this->assertSame('ok', $USER->preference['_test_user_preferences_pref']);
1394         unset_user_preference('_test_user_preferences_pref');
1395         $this->assertTrue(!isset($USER->preference['_test_user_preferences_pref']));
1397         // Test 1333 char values (no need for unicode, there are already tests for that in DB tests).
1398         $longvalue = str_repeat('a', 1333);
1399         set_user_preference('_test_long_user_preference', $longvalue);
1400         $this->assertEquals($longvalue, get_user_preferences('_test_long_user_preference'));
1401         $this->assertEquals($longvalue,
1402             $DB->get_field('user_preferences', 'value', array('userid' => $USER->id, 'name' => '_test_long_user_preference')));
1404         // Test > 1333 char values, coding_exception expected.
1405         $longvalue = str_repeat('a', 1334);
1406         try {
1407             set_user_preference('_test_long_user_preference', $longvalue);
1408             $this->fail('Exception expected - longer than 1333 chars not allowed as preference value');
1409         } catch (moodle_exception $ex) {
1410             $this->assertInstanceOf('coding_exception', $ex);
1411         }
1413         // Test invalid params.
1414         try {
1415             set_user_preference('_test_user_preferences_pref', array());
1416             $this->fail('Exception expected - array not valid preference value');
1417         } catch (moodle_exception $ex) {
1418             $this->assertInstanceOf('coding_exception', $ex);
1419         }
1420         try {
1421             set_user_preference('_test_user_preferences_pref', new stdClass);
1422             $this->fail('Exception expected - class not valid preference value');
1423         } catch (moodle_exception $ex) {
1424             $this->assertInstanceOf('coding_exception', $ex);
1425         }
1426         try {
1427             set_user_preference('_test_user_preferences_pref', 1, array('xx' => 1));
1428             $this->fail('Exception expected - user instance expected');
1429         } catch (moodle_exception $ex) {
1430             $this->assertInstanceOf('coding_exception', $ex);
1431         }
1432         try {
1433             set_user_preference('_test_user_preferences_pref', 1, 'abc');
1434             $this->fail('Exception expected - user instance expected');
1435         } catch (moodle_exception $ex) {
1436             $this->assertInstanceOf('coding_exception', $ex);
1437         }
1438         try {
1439             set_user_preference('', 1);
1440             $this->fail('Exception expected - invalid name accepted');
1441         } catch (moodle_exception $ex) {
1442             $this->assertInstanceOf('coding_exception', $ex);
1443         }
1444         try {
1445             set_user_preference('1', 1);
1446             $this->fail('Exception expected - invalid name accepted');
1447         } catch (moodle_exception $ex) {
1448             $this->assertInstanceOf('coding_exception', $ex);
1449         }
1450     }
1452     public function test_get_extra_user_fields() {
1453         global $CFG, $USER, $DB;
1454         $this->resetAfterTest();
1456         $this->setAdminUser();
1458         // It would be really nice if there were a way to 'mock' has_capability
1459         // checks (either to return true or false) but as there is not, this
1460         // test doesn't test the capability check. Presumably, anyone running
1461         // unit tests will have the capability.
1462         $context = context_system::instance();
1464         // No fields.
1465         $CFG->showuseridentity = '';
1466         $this->assertEquals(array(), get_extra_user_fields($context));
1468         // One field.
1469         $CFG->showuseridentity = 'frog';
1470         $this->assertEquals(array('frog'), get_extra_user_fields($context));
1472         // Two fields.
1473         $CFG->showuseridentity = 'frog,zombie';
1474         $this->assertEquals(array('frog', 'zombie'), get_extra_user_fields($context));
1476         // No fields, except.
1477         $CFG->showuseridentity = '';
1478         $this->assertEquals(array(), get_extra_user_fields($context, array('frog')));
1480         // One field.
1481         $CFG->showuseridentity = 'frog';
1482         $this->assertEquals(array(), get_extra_user_fields($context, array('frog')));
1484         // Two fields.
1485         $CFG->showuseridentity = 'frog,zombie';
1486         $this->assertEquals(array('zombie'), get_extra_user_fields($context, array('frog')));
1487     }
1489     public function test_get_extra_user_fields_sql() {
1490         global $CFG, $USER, $DB;
1491         $this->resetAfterTest();
1493         $this->setAdminUser();
1495         $context = context_system::instance();
1497         // No fields.
1498         $CFG->showuseridentity = '';
1499         $this->assertSame('', get_extra_user_fields_sql($context));
1501         // One field.
1502         $CFG->showuseridentity = 'frog';
1503         $this->assertSame(', frog', get_extra_user_fields_sql($context));
1505         // Two fields with table prefix.
1506         $CFG->showuseridentity = 'frog,zombie';
1507         $this->assertSame(', u1.frog, u1.zombie', get_extra_user_fields_sql($context, 'u1'));
1509         // Two fields with field prefix.
1510         $CFG->showuseridentity = 'frog,zombie';
1511         $this->assertSame(', frog AS u_frog, zombie AS u_zombie',
1512             get_extra_user_fields_sql($context, '', 'u_'));
1514         // One field excluded.
1515         $CFG->showuseridentity = 'frog';
1516         $this->assertSame('', get_extra_user_fields_sql($context, '', '', array('frog')));
1518         // Two fields, one excluded, table+field prefix.
1519         $CFG->showuseridentity = 'frog,zombie';
1520         $this->assertEquals(', u1.zombie AS u_zombie',
1521             get_extra_user_fields_sql($context, 'u1', 'u_', array('frog')));
1522     }
1524     /**
1525      * Test some critical TZ/DST.
1526      *
1527      * This method tests some special TZ/DST combinations that were fixed
1528      * by MDL-38999. The tests are done by comparing the results of the
1529      * output using Moodle TZ/DST support and PHP native one.
1530      *
1531      * Note: If you don't trust PHP TZ/DST support, can verify the
1532      * harcoded expectations below with:
1533      * http://www.tools4noobs.com/online_tools/unix_timestamp_to_datetime/
1534      */
1535     public function test_some_moodle_special_dst() {
1536         $stamp = 1365386400; // 2013/04/08 02:00:00 GMT/UTC.
1538         // In Europe/Tallinn it was 2013/04/08 05:00:00.
1539         $expectation = '2013/04/08 05:00:00';
1540         $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC'));
1541         $phpdt->setTimezone(new DateTimeZone('Europe/Tallinn'));
1542         $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result.
1543         $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'Europe/Tallinn', false); // Moodle result.
1544         $this->assertSame($expectation, $phpres);
1545         $this->assertSame($expectation, $moodleres);
1547         // In St. Johns it was 2013/04/07 23:30:00.
1548         $expectation = '2013/04/07 23:30:00';
1549         $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC'));
1550         $phpdt->setTimezone(new DateTimeZone('America/St_Johns'));
1551         $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result.
1552         $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'America/St_Johns', false); // Moodle result.
1553         $this->assertSame($expectation, $phpres);
1554         $this->assertSame($expectation, $moodleres);
1556         $stamp = 1383876000; // 2013/11/08 02:00:00 GMT/UTC.
1558         // In Europe/Tallinn it was 2013/11/08 04:00:00.
1559         $expectation = '2013/11/08 04:00:00';
1560         $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC'));
1561         $phpdt->setTimezone(new DateTimeZone('Europe/Tallinn'));
1562         $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result.
1563         $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'Europe/Tallinn', false); // Moodle result.
1564         $this->assertSame($expectation, $phpres);
1565         $this->assertSame($expectation, $moodleres);
1567         // In St. Johns it was 2013/11/07 22:30:00.
1568         $expectation = '2013/11/07 22:30:00';
1569         $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC'));
1570         $phpdt->setTimezone(new DateTimeZone('America/St_Johns'));
1571         $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result.
1572         $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'America/St_Johns', false); // Moodle result.
1573         $this->assertSame($expectation, $phpres);
1574         $this->assertSame($expectation, $moodleres);
1575     }
1577     public function test_userdate() {
1578         global $USER, $CFG, $DB;
1579         $this->resetAfterTest();
1581         $this->setAdminUser();
1583         $testvalues = array(
1584             array(
1585                 'time' => '1309514400',
1586                 'usertimezone' => 'America/Moncton',
1587                 'timezone' => '0.0', // No dst offset.
1588                 'expectedoutput' => 'Friday, 1 July 2011, 10:00 AM'
1589             ),
1590             array(
1591                 'time' => '1309514400',
1592                 'usertimezone' => 'America/Moncton',
1593                 'timezone' => '99', // Dst offset and timezone offset.
1594                 'expectedoutput' => 'Friday, 1 July 2011, 7:00 AM'
1595             ),
1596             array(
1597                 'time' => '1309514400',
1598                 'usertimezone' => 'America/Moncton',
1599                 'timezone' => 'America/Moncton', // Dst offset and timezone offset.
1600                 'expectedoutput' => 'Friday, 1 July 2011, 7:00 AM'
1601             ),
1602             array(
1603                 'time' => '1293876000 ',
1604                 'usertimezone' => 'America/Moncton',
1605                 'timezone' => '0.0', // No dst offset.
1606                 'expectedoutput' => 'Saturday, 1 January 2011, 10:00 AM'
1607             ),
1608             array(
1609                 'time' => '1293876000 ',
1610                 'usertimezone' => 'America/Moncton',
1611                 'timezone' => '99', // No dst offset in jan, so just timezone offset.
1612                 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 AM'
1613             ),
1614             array(
1615                 'time' => '1293876000 ',
1616                 'usertimezone' => 'America/Moncton',
1617                 'timezone' => 'America/Moncton', // No dst offset in jan.
1618                 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 AM'
1619             ),
1620             array(
1621                 'time' => '1293876000 ',
1622                 'usertimezone' => '2',
1623                 'timezone' => '99', // Take user timezone.
1624                 'expectedoutput' => 'Saturday, 1 January 2011, 12:00 PM'
1625             ),
1626             array(
1627                 'time' => '1293876000 ',
1628                 'usertimezone' => '-2',
1629                 'timezone' => '99', // Take user timezone.
1630                 'expectedoutput' => 'Saturday, 1 January 2011, 8:00 AM'
1631             ),
1632             array(
1633                 'time' => '1293876000 ',
1634                 'usertimezone' => '-10',
1635                 'timezone' => '2', // Take this timezone.
1636                 'expectedoutput' => 'Saturday, 1 January 2011, 12:00 PM'
1637             ),
1638             array(
1639                 'time' => '1293876000 ',
1640                 'usertimezone' => '-10',
1641                 'timezone' => '-2', // Take this timezone.
1642                 'expectedoutput' => 'Saturday, 1 January 2011, 8:00 AM'
1643             ),
1644             array(
1645                 'time' => '1293876000 ',
1646                 'usertimezone' => '-10',
1647                 'timezone' => 'random/time', // This should show server time.
1648                 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 PM'
1649             ),
1650             array(
1651                 'time' => '1293876000 ',
1652                 'usertimezone' => '14', // Server time zone.
1653                 'timezone' => '99',     // This should show user time.
1654                 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 PM'
1655             ),
1656         );
1658         // Check if forcetimezone is set then save it and set it to use user timezone.
1659         $cfgforcetimezone = null;
1660         if (isset($CFG->forcetimezone)) {
1661             $cfgforcetimezone = $CFG->forcetimezone;
1662             $CFG->forcetimezone = 99; // Get user default timezone.
1663         }
1664         // Store user default timezone to restore later.
1665         $userstimezone = $USER->timezone;
1667         // The string version of date comes from server locale setting and does
1668         // not respect user language, so it is necessary to reset that.
1669         $oldlocale = setlocale(LC_TIME, '0');
1670         setlocale(LC_TIME, 'en_AU.UTF-8');
1672         // Set default timezone to Australia/Perth, else time calculated
1673         // will not match expected values. Before that save system defaults.
1674         $systemdefaulttimezone = date_default_timezone_get();
1675         date_default_timezone_set('Australia/Perth');
1677         foreach ($testvalues as $vals) {
1678             $USER->timezone = $vals['usertimezone'];
1679             $actualoutput = userdate($vals['time'], '%A, %d %B %Y, %I:%M %p', $vals['timezone']);
1681             // On different systems case of AM PM changes so compare case insensitive.
1682             $vals['expectedoutput'] = textlib::strtolower($vals['expectedoutput']);
1683             $actualoutput = textlib::strtolower($actualoutput);
1685             $this->assertSame($vals['expectedoutput'], $actualoutput,
1686                 "Expected: {$vals['expectedoutput']} => Actual: {$actualoutput},
1687                 Please check if timezones are updated (Site adminstration -> location -> update timezone)");
1688         }
1690         // Restore user timezone back to what it was.
1691         $USER->timezone = $userstimezone;
1693         // Restore forcetimezone.
1694         if (!is_null($cfgforcetimezone)) {
1695             $CFG->forcetimezone = $cfgforcetimezone;
1696         }
1698         // Restore system default values.
1699         date_default_timezone_set($systemdefaulttimezone);
1700         setlocale(LC_TIME, $oldlocale);
1701     }
1703     public function test_make_timestamp() {
1704         global $USER, $CFG, $DB;
1705         $this->resetAfterTest();
1707         $this->setAdminUser();
1709         $testvalues = array(
1710             array(
1711                 'usertimezone' => 'America/Moncton',
1712                 'year' => '2011',
1713                 'month' => '7',
1714                 'day' => '1',
1715                 'hour' => '10',
1716                 'minutes' => '00',
1717                 'seconds' => '00',
1718                 'timezone' => '0.0',
1719                 'applydst' => false, // No dst offset.
1720                 'expectedoutput' => '1309514400' // 6pm at UTC+0.
1721             ),
1722             array(
1723                 'usertimezone' => 'America/Moncton',
1724                 'year' => '2011',
1725                 'month' => '7',
1726                 'day' => '1',
1727                 'hour' => '10',
1728                 'minutes' => '00',
1729                 'seconds' => '00',
1730                 'timezone' => '99',  // User default timezone.
1731                 'applydst' => false, // Don't apply dst.
1732                 'expectedoutput' => '1309528800'
1733             ),
1734             array(
1735                 'usertimezone' => 'America/Moncton',
1736                 'year' => '2011',
1737                 'month' => '7',
1738                 'day' => '1',
1739                 'hour' => '10',
1740                 'minutes' => '00',
1741                 'seconds' => '00',
1742                 'timezone' => '99', // User default timezone.
1743                 'applydst' => true, // Apply dst.
1744                 'expectedoutput' => '1309525200'
1745             ),
1746             array(
1747                 'usertimezone' => 'America/Moncton',
1748                 'year' => '2011',
1749                 'month' => '7',
1750                 'day' => '1',
1751                 'hour' => '10',
1752                 'minutes' => '00',
1753                 'seconds' => '00',
1754                 'timezone' => 'America/Moncton', // String timezone.
1755                 'applydst' => true, // Apply dst.
1756                 'expectedoutput' => '1309525200'
1757             ),
1758             array(
1759                 'usertimezone' => '2', // No dst applyed.
1760                 'year' => '2011',
1761                 'month' => '7',
1762                 'day' => '1',
1763                 'hour' => '10',
1764                 'minutes' => '00',
1765                 'seconds' => '00',
1766                 'timezone' => '99', // Take user timezone.
1767                 'applydst' => true, // Apply dst.
1768                 'expectedoutput' => '1309507200'
1769             ),
1770             array(
1771                 'usertimezone' => '-2', // No dst applyed.
1772                 'year' => '2011',
1773                 'month' => '7',
1774                 'day' => '1',
1775                 'hour' => '10',
1776                 'minutes' => '00',
1777                 'seconds' => '00',
1778                 'timezone' => '99', // Take usertimezone.
1779                 'applydst' => true, // Apply dst.
1780                 'expectedoutput' => '1309521600'
1781             ),
1782             array(
1783                 'usertimezone' => '-10', // No dst applyed.
1784                 'year' => '2011',
1785                 'month' => '7',
1786                 'day' => '1',
1787                 'hour' => '10',
1788                 'minutes' => '00',
1789                 'seconds' => '00',
1790                 'timezone' => '2',  // Take this timezone.
1791                 'applydst' => true, // Apply dst.
1792                 'expectedoutput' => '1309507200'
1793             ),
1794             array(
1795                 'usertimezone' => '-10', // No dst applyed.
1796                 'year' => '2011',
1797                 'month' => '7',
1798                 'day' => '1',
1799                 'hour' => '10',
1800                 'minutes' => '00',
1801                 'seconds' => '00',
1802                 'timezone' => '-2', // Take this timezone.
1803                 'applydst' => true, // Apply dst.
1804                 'expectedoutput' => '1309521600'
1805             ),
1806             array(
1807                 'usertimezone' => '-10', // No dst applyed.
1808                 'year' => '2011',
1809                 'month' => '7',
1810                 'day' => '1',
1811                 'hour' => '10',
1812                 'minutes' => '00',
1813                 'seconds' => '00',
1814                 'timezone' => 'random/time', // This should show server time.
1815                 'applydst' => true,          // Apply dst.
1816                 'expectedoutput' => '1309485600'
1817             ),
1818             array(
1819                 'usertimezone' => '14', // Server time.
1820                 'year' => '2011',
1821                 'month' => '7',
1822                 'day' => '1',
1823                 'hour' => '10',
1824                 'minutes' => '00',
1825                 'seconds' => '00',
1826                 'timezone' => '99', // Get user time.
1827                 'applydst' => true, // Apply dst.
1828                 'expectedoutput' => '1309485600'
1829             )
1830         );
1832         // Check if forcetimezone is set then save it and set it to use user timezone.
1833         $cfgforcetimezone = null;
1834         if (isset($CFG->forcetimezone)) {
1835             $cfgforcetimezone = $CFG->forcetimezone;
1836             $CFG->forcetimezone = 99; // Get user default timezone.
1837         }
1839         // Store user default timezone to restore later.
1840         $userstimezone = $USER->timezone;
1842         // The string version of date comes from server locale setting and does
1843         // not respect user language, so it is necessary to reset that.
1844         $oldlocale = setlocale(LC_TIME, '0');
1845         setlocale(LC_TIME, 'en_AU.UTF-8');
1847         // Set default timezone to Australia/Perth, else time calculated
1848         // Will not match expected values. Before that save system defaults.
1849         $systemdefaulttimezone = date_default_timezone_get();
1850         date_default_timezone_set('Australia/Perth');
1852         // Test make_timestamp with all testvals and assert if anything wrong.
1853         foreach ($testvalues as $vals) {
1854             $USER->timezone = $vals['usertimezone'];
1855             $actualoutput = make_timestamp(
1856                 $vals['year'],
1857                 $vals['month'],
1858                 $vals['day'],
1859                 $vals['hour'],
1860                 $vals['minutes'],
1861                 $vals['seconds'],
1862                 $vals['timezone'],
1863                 $vals['applydst']
1864             );
1866             // On different systems case of AM PM changes so compare case insensitive.
1867             $vals['expectedoutput'] = textlib::strtolower($vals['expectedoutput']);
1868             $actualoutput = textlib::strtolower($actualoutput);
1870             $this->assertSame($vals['expectedoutput'], $actualoutput,
1871                 "Expected: {$vals['expectedoutput']} => Actual: {$actualoutput},
1872                 Please check if timezones are updated (Site adminstration -> location -> update timezone)");
1873         }
1875         // Restore user timezone back to what it was.
1876         $USER->timezone = $userstimezone;
1878         // Restore forcetimezone.
1879         if (!is_null($cfgforcetimezone)) {
1880             $CFG->forcetimezone = $cfgforcetimezone;
1881         }
1883         // Restore system default values.
1884         date_default_timezone_set($systemdefaulttimezone);
1885         setlocale(LC_TIME, $oldlocale);
1886     }
1888     /**
1889      * Test get_string and most importantly the implementation of the lang_string
1890      * object.
1891      */
1892     public function test_get_string() {
1893         global $COURSE;
1895         // Make sure we are using English.
1896         $originallang = $COURSE->lang;
1897         $COURSE->lang = 'en';
1899         $yes = get_string('yes');
1900         $yesexpected = 'Yes';
1901         $this->assertSame('string', gettype($yes));
1902         $this->assertSame($yesexpected, $yes);
1904         $yes = get_string('yes', 'moodle');
1905         $this->assertSame('string', gettype($yes));
1906         $this->assertSame($yesexpected, $yes);
1908         $yes = get_string('yes', 'core');
1909         $this->assertSame('string', gettype($yes));
1910         $this->assertSame($yesexpected, $yes);
1912         $yes = get_string('yes', '');
1913         $this->assertSame('string', gettype($yes));
1914         $this->assertSame($yesexpected, $yes);
1916         $yes = get_string('yes', null);
1917         $this->assertSame('string', gettype($yes));
1918         $this->assertSame($yesexpected, $yes);
1920         $yes = get_string('yes', null, 1);
1921         $this->assertSame('string', gettype($yes));
1922         $this->assertSame($yesexpected, $yes);
1924         $days = 1;
1925         $numdays = get_string('numdays', 'core', '1');
1926         $numdaysexpected = $days.' days';
1927         $this->assertSame('string', gettype($numdays));
1928         $this->assertSame($numdaysexpected, $numdays);
1930         $yes = get_string('yes', null, null, true);
1931         $this->assertSame('lang_string', get_class($yes));
1932         $this->assertSame($yesexpected, (string)$yes);
1934         // Test using a lang_string object as the $a argument for a normal
1935         // get_string call (returning string).
1936         $test = new lang_string('yes', null, null, true);
1937         $testexpected = get_string('numdays', 'core', get_string('yes'));
1938         $testresult = get_string('numdays', null, $test);
1939         $this->assertSame('string', gettype($testresult));
1940         $this->assertSame($testexpected, $testresult);
1942         // Test using a lang_string object as the $a argument for an object
1943         // get_string call (returning lang_string).
1944         $test = new lang_string('yes', null, null, true);
1945         $testexpected = get_string('numdays', 'core', get_string('yes'));
1946         $testresult = get_string('numdays', null, $test, true);
1947         $this->assertSame('lang_string', get_class($testresult));
1948         $this->assertSame($testexpected, "$testresult");
1950         // Make sure that object properties that can't be converted don't cause
1951         // errors.
1952         // Level one: This is as deep as current language processing goes.
1953         $test = new stdClass;
1954         $test->one = 'here';
1955         $string = get_string('yes', null, $test, true);
1956         $this->assertEquals($yesexpected, $string);
1958         // Make sure that object properties that can't be converted don't cause
1959         // errors.
1960         // Level two: Language processing doesn't currently reach this deep.
1961         // only immediate scalar properties are worked with.
1962         $test = new stdClass;
1963         $test->one = new stdClass;
1964         $test->one->two = 'here';
1965         $string = get_string('yes', null, $test, true);
1966         $this->assertEquals($yesexpected, $string);
1968         // Make sure that object properties that can't be converted don't cause
1969         // errors.
1970         // Level three: It should never ever go this deep, but we're making sure
1971         // it doesn't cause any probs anyway.
1972         $test = new stdClass;
1973         $test->one = new stdClass;
1974         $test->one->two = new stdClass;
1975         $test->one->two->three = 'here';
1976         $string = get_string('yes', null, $test, true);
1977         $this->assertEquals($yesexpected, $string);
1979         // Make sure that object properties that can't be converted don't cause
1980         // errors and check lang_string properties.
1981         // Level one: This is as deep as current language processing goes.
1982         $test = new stdClass;
1983         $test->one = new lang_string('yes');
1984         $string = get_string('yes', null, $test, true);
1985         $this->assertEquals($yesexpected, $string);
1987         // Make sure that object properties that can't be converted don't cause
1988         // errors and check lang_string properties.
1989         // Level two: Language processing doesn't currently reach this deep.
1990         // only immediate scalar properties are worked with.
1991         $test = new stdClass;
1992         $test->one = new stdClass;
1993         $test->one->two = new lang_string('yes');
1994         $string = get_string('yes', null, $test, true);
1995         $this->assertEquals($yesexpected, $string);
1997         // Make sure that object properties that can't be converted don't cause
1998         // errors and check lang_string properties.
1999         // Level three: It should never ever go this deep, but we're making sure
2000         // it doesn't cause any probs anyway.
2001         $test = new stdClass;
2002         $test->one = new stdClass;
2003         $test->one->two = new stdClass;
2004         $test->one->two->three = new lang_string('yes');
2005         $string = get_string('yes', null, $test, true);
2006         $this->assertEquals($yesexpected, $string);
2008         // Make sure that array properties that can't be converted don't cause
2009         // errors.
2010         $test = array();
2011         $test['one'] = new stdClass;
2012         $test['one']->two = 'here';
2013         $string = get_string('yes', null, $test, true);
2014         $this->assertEquals($yesexpected, $string);
2016         // Same thing but as above except using an object... this is allowed :P.
2017         $string = get_string('yes', null, null, true);
2018         $object = new stdClass;
2019         $object->$string = 'Yes';
2020         $this->assertEquals($yesexpected, $string);
2021         $this->assertEquals($yesexpected, $object->$string);
2023         // Reset the language.
2024         $COURSE->lang = $originallang;
2025     }
2027     /**
2028      * @expectedException PHPUnit_Framework_Error_Warning
2029      */
2030     public function test_get_string_limitation() {
2031         // This is one of the limitations to the lang_string class. It can't be
2032         // used as a key.
2033         $array = array(get_string('yes', null, null, true) => 'yes');
2034     }
2036     /**
2037      * Test localised float formatting.
2038      */
2039     public function test_format_float() {
2041         // Special case for null.
2042         $this->assertEquals('', format_float(null));
2044         // Default 1 decimal place.
2045         $this->assertEquals('5.4', format_float(5.43));
2046         $this->assertEquals('5.0', format_float(5.001));
2048         // Custom number of decimal places.
2049         $this->assertEquals('5.43000', format_float(5.43, 5));
2051         // Option to strip ending zeros after rounding.
2052         $this->assertEquals('5.43', format_float(5.43, 5, true, true));
2053         $this->assertEquals('5', format_float(5.0001, 3, true, true));
2055         // Tests with a localised decimal separator.
2056         $this->define_local_decimal_separator();
2058         // Localisation on (default).
2059         $this->assertEquals('5X43000', format_float(5.43, 5));
2060         $this->assertEquals('5X43', format_float(5.43, 5, true, true));
2062         // Localisation off.
2063         $this->assertEquals('5.43000', format_float(5.43, 5, false));
2064         $this->assertEquals('5.43', format_float(5.43, 5, false, true));
2065     }
2067     /**
2068      * Test localised float unformatting.
2069      */
2070     public function test_unformat_float() {
2072         // Tests without the localised decimal separator.
2074         // Special case for null, empty or white spaces only strings.
2075         $this->assertEquals(null, unformat_float(null));
2076         $this->assertEquals(null, unformat_float(''));
2077         $this->assertEquals(null, unformat_float('    '));
2079         // Regular use.
2080         $this->assertEquals(5.4, unformat_float('5.4'));
2081         $this->assertEquals(5.4, unformat_float('5.4', true));
2083         // No decimal.
2084         $this->assertEquals(5.0, unformat_float('5'));
2086         // Custom number of decimal.
2087         $this->assertEquals(5.43267, unformat_float('5.43267'));
2089         // Empty decimal.
2090         $this->assertEquals(100.0, unformat_float('100.00'));
2092         // With the thousand separator.
2093         $this->assertEquals(1000.0, unformat_float('1 000'));
2094         $this->assertEquals(1000.32, unformat_float('1 000.32'));
2096         // Negative number.
2097         $this->assertEquals(-100.0, unformat_float('-100'));
2099         // Wrong value.
2100         $this->assertEquals(0.0, unformat_float('Wrong value'));
2101         // Wrong value in strict mode.
2102         $this->assertFalse(unformat_float('Wrong value', true));
2104         // Combining options.
2105         $this->assertEquals(-1023.862567, unformat_float('   -1 023.862567     '));
2107         // Bad decimal separator (should crop the decimal).
2108         $this->assertEquals(50.0, unformat_float('50,57'));
2109         // Bad decimal separator in strict mode (should return false).
2110         $this->assertFalse(unformat_float('50,57', true));
2112         // Tests with a localised decimal separator.
2113         $this->define_local_decimal_separator();
2115         // We repeat the tests above but with the current decimal separator.
2117         // Regular use without and with the localised separator.
2118         $this->assertEquals (5.4, unformat_float('5.4'));
2119         $this->assertEquals (5.4, unformat_float('5X4'));
2121         // Custom number of decimal.
2122         $this->assertEquals (5.43267, unformat_float('5X43267'));
2124         // Empty decimal.
2125         $this->assertEquals (100.0, unformat_float('100X00'));
2127         // With the thousand separator.
2128         $this->assertEquals (1000.32, unformat_float('1 000X32'));
2130         // Bad different separator (should crop the decimal).
2131         $this->assertEquals (50.0, unformat_float('50Y57'));
2132         // Bad different separator in strict mode (should return false).
2133         $this->assertFalse (unformat_float('50Y57', true));
2135         // Combining options.
2136         $this->assertEquals (-1023.862567, unformat_float('   -1 023X862567     '));
2137         // Combining options in strict mode.
2138         $this->assertEquals (-1023.862567, unformat_float('   -1 023X862567     ', true));
2139     }
2141     /**
2142      * Test deleting of users.
2143      */
2144     public function test_delete_user() {
2145         global $DB, $CFG;
2147         $this->resetAfterTest();
2149         $guest = $DB->get_record('user', array('id'=>$CFG->siteguest), '*', MUST_EXIST);
2150         $admin = $DB->get_record('user', array('id'=>$CFG->siteadmins), '*', MUST_EXIST);
2151         $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
2153         $user = $this->getDataGenerator()->create_user(array('idnumber'=>'abc'));
2154         $user2 = $this->getDataGenerator()->create_user(array('idnumber'=>'xyz'));
2156         $result = delete_user($user);
2157         $this->assertTrue($result);
2158         $deluser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST);
2159         $this->assertEquals(1, $deluser->deleted);
2160         $this->assertEquals(0, $deluser->picture);
2161         $this->assertSame('', $deluser->idnumber);
2162         $this->assertSame(md5($user->username), $deluser->email);
2163         $this->assertRegExp('/^'.preg_quote($user->email, '/').'\.\d*$/', $deluser->username);
2165         $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
2167         // Try invalid params.
2169         $record = new stdClass();
2170         $record->grrr = 1;
2171         try {
2172             delete_user($record);
2173             $this->fail('Expecting exception for invalid delete_user() $user parameter');
2174         } catch (moodle_exception $ex) {
2175             $this->assertInstanceOf('coding_exception', $ex);
2176         }
2177         $record->id = 1;
2178         try {
2179             delete_user($record);
2180             $this->fail('Expecting exception for invalid delete_user() $user parameter');
2181         } catch (moodle_exception $ex) {
2182             $this->assertInstanceOf('coding_exception', $ex);
2183         }
2185         $record = new stdClass();
2186         $record->id = 666;
2187         $record->username = 'xx';
2188         $this->assertFalse($DB->record_exists('user', array('id'=>666))); // Any non-existent id is ok.
2189         $result = delete_user($record);
2190         $this->assertFalse($result);
2192         $result = delete_user($guest);
2193         $this->assertFalse($result);
2195         $result = delete_user($admin);
2196         $this->assertFalse($result);
2198         $this->resetDebugging();
2199     }
2201     /**
2202      * Test function convert_to_array()
2203      */
2204     public function test_convert_to_array() {
2205         // Check that normal classes are converted to arrays the same way as (array) would do.
2206         $obj = new stdClass();
2207         $obj->prop1 = 'hello';
2208         $obj->prop2 = array('first', 'second', 13);
2209         $obj->prop3 = 15;
2210         $this->assertEquals(convert_to_array($obj), (array)$obj);
2212         // Check that context object (with iterator) is converted to array properly.
2213         $obj = context_system::instance();
2214         $ar = array(
2215             'id'           => $obj->id,
2216             'contextlevel' => $obj->contextlevel,
2217             'instanceid'   => $obj->instanceid,
2218             'path'         => $obj->path,
2219             'depth'        => $obj->depth
2220         );
2221         $this->assertEquals(convert_to_array($obj), $ar);
2222     }
2224     /**
2225      * Test the function date_format_string().
2226      */
2227     public function test_date_format_string() {
2228         global $CFG;
2230         // Forcing locale and timezone.
2231         $oldlocale = setlocale(LC_TIME, '0');
2232         if ($CFG->ostype == 'WINDOWS') {
2233             setlocale(LC_TIME, 'English_Australia.1252');
2234         } else {
2235             setlocale(LC_TIME, 'en_AU.UTF-8');
2236         }
2237         $systemdefaulttimezone = date_default_timezone_get();
2238         date_default_timezone_set('Australia/Perth');
2240         $tests = array(
2241             array(
2242                 'tz' => 99,
2243                 'str' => '%A, %d %B %Y, %I:%M %p',
2244                 'expected' => 'Saturday, 01 January 2011, 06:00 PM'
2245             ),
2246             array(
2247                 'tz' => 0,
2248                 'str' => '%A, %d %B %Y, %I:%M %p',
2249                 'expected' => 'Saturday, 01 January 2011, 10:00 AM'
2250             ),
2251             array(
2252                 'tz' => -12,
2253                 'str' => '%A, %d %B %Y, %I:%M %p',
2254                 'expected' => 'Saturday, 01 January 2011, 10:00 AM'
2255             ),
2256             // Following tests pass on Windows only because en lang pack does
2257             // not contain localewincharset, in real life lang pack maintainers
2258             // may use only characters that are present in localewincharset
2259             // in format strings!
2260             array(
2261                 'tz' => 99,
2262                 'str' => 'Žluťoučký koníček %A',
2263                 'expected' => 'Žluťoučký koníček Saturday'
2264             ),
2265             array(
2266                 'tz' => 99,
2267                 'str' => '言語設定言語 %A',
2268                 'expected' => '言語設定言語 Saturday'
2269             ),
2270             array(
2271                 'tz' => 99,
2272                 'str' => '简体中文简体 %A',
2273                 'expected' => '简体中文简体 Saturday'
2274             ),
2275         );
2277         // Note: date_format_string() uses the timezone only to differenciate
2278         // the server time from the UTC time. It does not modify the timestamp.
2279         // Hence similar results for timezones <= 13.
2280         // On different systems case of AM PM changes so compare case insensitive.
2281         foreach ($tests as $test) {
2282             $str = date_format_string(1293876000, $test['str'], $test['tz']);
2283             $this->assertSame(textlib::strtolower($test['expected']), textlib::strtolower($str));
2284         }
2286         // Restore system default values.
2287         date_default_timezone_set($systemdefaulttimezone);
2288         setlocale(LC_TIME, $oldlocale);
2289     }
2291     public function test_get_config() {
2292         global $CFG;
2294         $this->resetAfterTest();
2296         // Preparation.
2297         set_config('phpunit_test_get_config_1', 'test 1');
2298         set_config('phpunit_test_get_config_2', 'test 2', 'mod_forum');
2299         if (!is_array($CFG->config_php_settings)) {
2300             $CFG->config_php_settings = array();
2301         }
2302         $CFG->config_php_settings['phpunit_test_get_config_3'] = 'test 3';
2304         if (!is_array($CFG->forced_plugin_settings)) {
2305             $CFG->forced_plugin_settings = array();
2306         }
2307         if (!array_key_exists('mod_forum', $CFG->forced_plugin_settings)) {
2308             $CFG->forced_plugin_settings['mod_forum'] = array();
2309         }
2310         $CFG->forced_plugin_settings['mod_forum']['phpunit_test_get_config_4'] = 'test 4';
2311         $CFG->phpunit_test_get_config_5 = 'test 5';
2313         // Testing.
2314         $this->assertSame('test 1', get_config('core', 'phpunit_test_get_config_1'));
2315         $this->assertSame('test 2', get_config('mod_forum', 'phpunit_test_get_config_2'));
2316         $this->assertSame('test 3', get_config('core', 'phpunit_test_get_config_3'));
2317         $this->assertSame('test 4', get_config('mod_forum', 'phpunit_test_get_config_4'));
2318         $this->assertFalse(get_config('core', 'phpunit_test_get_config_5'));
2319         $this->assertFalse(get_config('core', 'phpunit_test_get_config_x'));
2320         $this->assertFalse(get_config('mod_forum', 'phpunit_test_get_config_x'));
2322         // Test config we know to exist.
2323         $this->assertSame($CFG->dataroot, get_config('core', 'dataroot'));
2324         $this->assertSame($CFG->phpunit_dataroot, get_config('core', 'phpunit_dataroot'));
2325         $this->assertSame($CFG->dataroot, get_config('core', 'phpunit_dataroot'));
2326         $this->assertSame(get_config('core', 'dataroot'), get_config('core', 'phpunit_dataroot'));
2328         // Test setting a config var that already exists.
2329         set_config('phpunit_test_get_config_1', 'test a');
2330         $this->assertSame('test a', $CFG->phpunit_test_get_config_1);
2331         $this->assertSame('test a', get_config('core', 'phpunit_test_get_config_1'));
2333         // Test cache invalidation.
2334         $cache = cache::make('core', 'config');
2335         $this->assertInternalType('array', $cache->get('core'));
2336         $this->assertInternalType('array', $cache->get('mod_forum'));
2337         set_config('phpunit_test_get_config_1', 'test b');
2338         $this->assertFalse($cache->get('core'));
2339         set_config('phpunit_test_get_config_4', 'test c', 'mod_forum');
2340         $this->assertFalse($cache->get('mod_forum'));
2341     }
2343     public function test_get_max_upload_sizes() {
2344         // Test with very low limits so we are not affected by php upload limits.
2345         // Test activity limit smallest.
2346         $sitebytes = 102400;
2347         $coursebytes = 51200;
2348         $modulebytes = 10240;
2349         $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
2351         $this->assertSame('Activity upload limit (10KB)', $result['0']);
2352         $this->assertCount(2, $result);
2354         // Test course limit smallest.
2355         $sitebytes = 102400;
2356         $coursebytes = 10240;
2357         $modulebytes = 51200;
2358         $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
2360         $this->assertSame('Course upload limit (10KB)', $result['0']);
2361         $this->assertCount(2, $result);
2363         // Test site limit smallest.
2364         $sitebytes = 10240;
2365         $coursebytes = 102400;
2366         $modulebytes = 51200;
2367         $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
2369         $this->assertSame('Site upload limit (10KB)', $result['0']);
2370         $this->assertCount(2, $result);
2372         // Test site limit not set.
2373         $sitebytes = 0;
2374         $coursebytes = 102400;
2375         $modulebytes = 51200;
2376         $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
2378         $this->assertSame('Activity upload limit (50KB)', $result['0']);
2379         $this->assertCount(3, $result);
2381         $sitebytes = 0;
2382         $coursebytes = 51200;
2383         $modulebytes = 102400;
2384         $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
2386         $this->assertSame('Course upload limit (50KB)', $result['0']);
2387         $this->assertCount(3, $result);
2389         // Test custom bytes in range.
2390         $sitebytes = 102400;
2391         $coursebytes = 51200;
2392         $modulebytes = 51200;
2393         $custombytes = 10240;
2394         $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes);
2396         $this->assertCount(3, $result);
2398         // Test custom bytes in range but non-standard.
2399         $sitebytes = 102400;
2400         $coursebytes = 51200;
2401         $modulebytes = 51200;
2402         $custombytes = 25600;
2403         $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes);
2405         $this->assertCount(4, $result);
2407         // Test custom bytes out of range.
2408         $sitebytes = 102400;
2409         $coursebytes = 51200;
2410         $modulebytes = 51200;
2411         $custombytes = 102400;
2412         $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes);
2414         $this->assertCount(3, $result);
2416         // Test custom bytes out of range and non-standard.
2417         $sitebytes = 102400;
2418         $coursebytes = 51200;
2419         $modulebytes = 51200;
2420         $custombytes = 256000;
2421         $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes);
2423         $this->assertCount(3, $result);
2425         // Test site limit only.
2426         $sitebytes = 51200;
2427         $result = get_max_upload_sizes($sitebytes);
2429         $this->assertSame('Site upload limit (50KB)', $result['0']);
2430         $this->assertSame('50KB', $result['51200']);
2431         $this->assertSame('10KB', $result['10240']);
2432         $this->assertCount(3, $result);
2434         // Test no limit.
2435         $result = get_max_upload_sizes();
2436         $this->assertArrayHasKey('0', $result);
2437         $this->assertArrayHasKey(get_max_upload_file_size(), $result);
2438     }
2440     /**
2441      * Test function password_is_legacy_hash().
2442      */
2443     public function test_password_is_legacy_hash() {
2444         // Well formed md5s should be matched.
2445         foreach (array('some', 'strings', 'to_check!') as $string) {
2446             $md5 = md5($string);
2447             $this->assertTrue(password_is_legacy_hash($md5));
2448         }
2449         // Strings that are not md5s should not be matched.
2450         foreach (array('', AUTH_PASSWORD_NOT_CACHED, 'IPW8WTcsWNgAWcUS1FBVHegzJnw5M2jOmYkmfc8z.xdBOyC4Caeum') as $notmd5) {
2451             $this->assertFalse(password_is_legacy_hash($notmd5));
2452         }
2453     }
2455     /**
2456      * Test function validate_internal_user_password().
2457      */
2458     public function test_validate_internal_user_password() {
2459         if (password_compat_not_supported()) {
2460             // If bcrypt is not properly supported test legacy md5 hashes instead.
2461             // Can't hardcode these as we don't know the site's password salt.
2462             $validhashes = array(
2463                 'pw' => hash_internal_user_password('pw'),
2464                 'abc' => hash_internal_user_password('abc'),
2465                 'C0mP1eX_&}<?@*&%` |\"' => hash_internal_user_password('C0mP1eX_&}<?@*&%` |\"'),
2466                 'ĩńťėŕňăţĩōŋāĹ' => hash_internal_user_password('ĩńťėŕňăţĩōŋāĹ')
2467             );
2468         } else {
2469             // Otherwise test bcrypt hashes.
2470             $validhashes = array(
2471                 'pw' => '$2y$10$LOSDi5eaQJhutSRun.OVJ.ZSxQZabCMay7TO1KmzMkDMPvU40zGXK',
2472                 'abc' => '$2y$10$VWTOhVdsBbWwtdWNDRHSpewjd3aXBQlBQf5rBY/hVhw8hciarFhXa',
2473                 'C0mP1eX_&}<?@*&%` |\"' => '$2y$10$3PJf.q.9ywNJlsInPbqc8.IFeSsvXrGvQLKRFBIhVu1h1I3vpIry6',
2474                 'ĩńťėŕňăţĩōŋāĹ' => '$2y$10$3A2Y8WpfRAnP3czJiSv6N.6Xp0T8hW3QZz2hUCYhzyWr1kGP1yUve'
2475             );
2476         }
2478         foreach ($validhashes as $password => $hash) {
2479             $user = new stdClass();
2480             $user->auth = 'manual';
2481             $user->password = $hash;
2482             // The correct password should be validated.
2483             $this->assertTrue(validate_internal_user_password($user, $password));
2484             // An incorrect password should not be validated.
2485             $this->assertFalse(validate_internal_user_password($user, 'badpw'));
2486         }
2487     }
2489     /**
2490      * Test function hash_internal_user_password().
2491      */
2492     public function test_hash_internal_user_password() {
2493         $passwords = array('pw', 'abc123', 'C0mP1eX_&}<?@*&%` |\"', 'ĩńťėŕňăţĩōŋāĹ');
2495         // Check that some passwords that we convert to hashes can
2496         // be validated.
2497         foreach ($passwords as $password) {
2498             $hash = hash_internal_user_password($password);
2499             $fasthash = hash_internal_user_password($password, true);
2500             $user = new stdClass();
2501             $user->auth = 'manual';
2502             $user->password = $hash;
2503             $this->assertTrue(validate_internal_user_password($user, $password));
2505             if (password_compat_not_supported()) {
2506                 // If bcrypt is not properly supported make sure the passwords are in md5 format.
2507                 $this->assertTrue(password_is_legacy_hash($hash));
2508             } else {
2509                 // Otherwise they should not be in md5 format.
2510                 $this->assertFalse(password_is_legacy_hash($hash));
2512                 // Check that cost factor in hash is correctly set.
2513                 $this->assertRegExp('/\$10\$/', $hash);
2514                 $this->assertRegExp('/\$04\$/', $fasthash);
2515             }
2516         }
2517     }
2519     /**
2520      * Test function update_internal_user_password().
2521      */
2522     public function test_update_internal_user_password() {
2523         global $DB;
2524         $this->resetAfterTest();
2525         $passwords = array('password', '1234', 'changeme', '****');
2526         foreach ($passwords as $password) {
2527             $user = $this->getDataGenerator()->create_user(array('auth'=>'manual'));
2528             update_internal_user_password($user, $password);
2529             // The user object should have been updated.
2530             $this->assertTrue(validate_internal_user_password($user, $password));
2531             // The database field for the user should also have been updated to the
2532             // same value.
2533             $this->assertSame($user->password, $DB->get_field('user', 'password', array('id' => $user->id)));
2534         }
2536         $user = $this->getDataGenerator()->create_user(array('auth'=>'manual'));
2537         // Manually set the user's password to the md5 of the string 'password'.
2538         $DB->set_field('user', 'password', '5f4dcc3b5aa765d61d8327deb882cf99', array('id' => $user->id));
2540         // Update the password.
2541         update_internal_user_password($user, 'password');
2543         if (password_compat_not_supported()) {
2544             // If bcrypt not properly supported the password should remain as an md5 hash.
2545             $expected_hash = hash_internal_user_password('password', true);
2546             $this->assertSame($user->password, $expected_hash);
2547             $this->assertTrue(password_is_legacy_hash($user->password));
2548         } else {
2549             // Otherwise password should have been updated to a bcrypt hash.
2550             $this->assertFalse(password_is_legacy_hash($user->password));
2551         }
2552     }
2554     public function test_fullname() {
2555         global $CFG;
2557         $this->resetAfterTest();
2559         // Create a user to test the name display on.
2560         $record = array();
2561         $record['firstname'] = 'Scott';
2562         $record['lastname'] = 'Fletcher';
2563         $record['firstnamephonetic'] = 'スコット';
2564         $record['lastnamephonetic'] = 'フレチャー';
2565         $record['alternatename'] = 'No friends';
2566         $user = $this->getDataGenerator()->create_user($record);
2568         // Back up config settings for restore later.
2569         $originalcfg = new stdClass();
2570         $originalcfg->fullnamedisplay = $CFG->fullnamedisplay;
2572         // Testing existing fullnamedisplay settings.
2573         $CFG->fullnamedisplay = 'firstname';
2574         $testname = fullname($user);
2575         $this->assertSame($user->firstname, $testname);
2577         $CFG->fullnamedisplay = 'firstname lastname';
2578         $expectedname = "$user->firstname $user->lastname";
2579         $testname = fullname($user);
2580         $this->assertSame($expectedname, $testname);
2582         $CFG->fullnamedisplay = 'lastname firstname';
2583         $expectedname = "$user->lastname $user->firstname";
2584         $testname = fullname($user);
2585         $this->assertSame($expectedname, $testname);
2587         $expectedname = get_string('fullnamedisplay', null, $user);
2588         $CFG->fullnamedisplay = 'language';
2589         $testname = fullname($user);
2590         $this->assertSame($expectedname, $testname);
2592         // Test override parameter.
2593         $CFG->fullnamedisplay = 'firstname';
2594         $expectedname = "$user->firstname $user->lastname";
2595         $testname = fullname($user, true);
2596         $this->assertSame($expectedname, $testname);
2598         // Test additional name fields.
2599         $CFG->fullnamedisplay = 'lastname lastnamephonetic firstname firstnamephonetic';
2600         $expectedname = "$user->lastname $user->lastnamephonetic $user->firstname $user->firstnamephonetic";
2601         $testname = fullname($user);
2602         $this->assertSame($expectedname, $testname);
2604         // Test for handling missing data.
2605         $user->middlename = null;
2606         // Parenthesis with no data.
2607         $CFG->fullnamedisplay = 'firstname (middlename) lastname';
2608         $expectedname = "$user->firstname $user->lastname";
2609         $testname = fullname($user);
2610         $this->assertSame($expectedname, $testname);
2612         // Extra spaces due to no data.
2613         $CFG->fullnamedisplay = 'firstname middlename lastname';
2614         $expectedname = "$user->firstname $user->lastname";
2615         $testname = fullname($user);
2616         $this->assertSame($expectedname, $testname);
2618         // Regular expression testing.
2619         // Remove some data from the user fields.
2620         $user->firstnamephonetic = '';
2621         $user->lastnamephonetic = '';
2623         // Removing empty brackets and excess whitespace.
2624         // All of these configurations should resolve to just firstname lastname.
2625         $configarray = array();
2626         $configarray[] = 'firstname lastname [firstnamephonetic lastnamephonetic]';
2627         $configarray[] = 'firstname lastname \'middlename\'';
2628         $configarray[] = 'firstname "firstnamephonetic" lastname';
2629         $configarray[] = 'firstname 「firstnamephonetic」 lastname 「lastnamephonetic」';
2631         foreach ($configarray as $config) {
2632             $CFG->fullnamedisplay = $config;
2633             $expectedname = "$user->firstname $user->lastname";
2634             $testname = fullname($user);
2635             $this->assertSame($expectedname, $testname);
2636         }
2638         // Check to make sure that other characters are left in place.
2639         $configarray = array();
2640         $configarray['0'] = new stdClass();
2641         $configarray['0']->config = 'lastname firstname, middlename';
2642         $configarray['0']->expectedname = "$user->lastname $user->firstname,";
2643         $configarray['1'] = new stdClass();
2644         $configarray['1']->config = 'lastname firstname + alternatename';
2645         $configarray['1']->expectedname = "$user->lastname $user->firstname + $user->alternatename";
2646         $configarray['2'] = new stdClass();
2647         $configarray['2']->config = 'firstname aka: alternatename';
2648         $configarray['2']->expectedname = "$user->firstname aka: $user->alternatename";
2649         $configarray['3'] = new stdClass();
2650         $configarray['3']->config = 'firstname (alternatename)';
2651         $configarray['3']->expectedname = "$user->firstname ($user->alternatename)";
2652         $configarray['4'] = new stdClass();
2653         $configarray['4']->config = 'firstname [alternatename]';
2654         $configarray['4']->expectedname = "$user->firstname [$user->alternatename]";
2655         $configarray['5'] = new stdClass();
2656         $configarray['5']->config = 'firstname "lastname"';
2657         $configarray['5']->expectedname = "$user->firstname \"$user->lastname\"";
2659         foreach ($configarray as $config) {
2660             $CFG->fullnamedisplay = $config->config;
2661             $expectedname = $config->expectedname;
2662             $testname = fullname($user);
2663             $this->assertSame($expectedname, $testname);
2664         }
2666         // Tidy up after we finish testing.
2667         $CFG->fullnamedisplay = $originalcfg->fullnamedisplay;
2668     }
2670     public function test_get_all_user_name_fields() {
2671         $this->resetAfterTest();
2673         // Additional names in an array.
2674         $testarray = array('firstnamephonetic',
2675                            'lastnamephonetic',
2676                            'middlename',
2677                            'alternatename',
2678                            'firstname',
2679                            'lastname');
2680         $this->assertEquals($testarray, get_all_user_name_fields());
2682         // Additional names as a string.
2683         $teststring = 'firstnamephonetic,lastnamephonetic,middlename,alternatename,firstname,lastname';
2684         $this->assertEquals($teststring, get_all_user_name_fields(true));
2686         // Additional names as a string with an alias.
2687         $teststring = 't.firstnamephonetic,t.lastnamephonetic,t.middlename,t.alternatename,t.firstname,t.lastname';
2688         $this->assertEquals($teststring, get_all_user_name_fields(true, 't'));
2689     }
2691     public function test_order_in_string() {
2692         $this->resetAfterTest();
2694         // Return an array in an order as they are encountered in a string.
2695         $valuearray = array('second', 'firsthalf', 'first');
2696         $formatstring = 'first firsthalf some other text (second)';
2697         $expectedarray = array('0' => 'first', '6' => 'firsthalf', '33' => 'second');
2698         $this->assertEquals($expectedarray, order_in_string($valuearray, $formatstring));
2700         // Try again with a different order for the format.
2701         $valuearray = array('second', 'firsthalf', 'first');
2702         $formatstring = 'firsthalf first second';
2703         $expectedarray = array('0' => 'firsthalf', '10' => 'first', '16' => 'second');
2704         $this->assertEquals($expectedarray, order_in_string($valuearray, $formatstring));
2706         // Try again with yet another different order for the format.
2707         $valuearray = array('second', 'firsthalf', 'first');
2708         $formatstring = 'start seconds away second firstquater first firsthalf';
2709         $expectedarray = array('19' => 'second', '38' => 'first', '44' => 'firsthalf');
2710         $this->assertEquals($expectedarray, order_in_string($valuearray, $formatstring));
2711     }