PageRenderTime 38ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/tests/moodlelib_test.php

https://bitbucket.org/synergylearning/campusconnect
PHP | 2721 lines | 1982 code | 366 blank | 373 comment | 21 complexity | 75765fc3f0f41f5d0e0645c456366842 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-3.0
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Unit tests for (some of) ../moodlelib.php.
  18. *
  19. * @package core
  20. * @category phpunit
  21. * @copyright &copy; 2006 The Open University
  22. * @author T.J.Hunt@open.ac.uk
  23. * @author nicolas@moodle.com
  24. */
  25. defined('MOODLE_INTERNAL') || die();
  26. class core_moodlelib_testcase extends advanced_testcase {
  27. public static $includecoverage = array('lib/moodlelib.php');
  28. /**
  29. * Define a local decimal separator.
  30. *
  31. * It is not possible to directly change the result of get_string in
  32. * a unit test. Instead, we create a language pack for language 'xx' in
  33. * dataroot and make langconfig.php with the string we need to change.
  34. * The example separator used here is 'X'; on PHP 5.3 and before this
  35. * must be a single byte character due to PHP bug/limitation in
  36. * number_format, so you can't use UTF-8 characters.
  37. */
  38. protected function define_local_decimal_separator() {
  39. global $SESSION, $CFG;
  40. $SESSION->lang = 'xx';
  41. $langconfig = "<?php\n\$string['decsep'] = 'X';";
  42. $langfolder = $CFG->dataroot . '/lang/xx';
  43. check_dir_exists($langfolder);
  44. file_put_contents($langfolder . '/langconfig.php', $langconfig);
  45. }
  46. public function test_cleanremoteaddr() {
  47. // IPv4.
  48. $this->assertNull(cleanremoteaddr('1023.121.234.1'));
  49. $this->assertSame('123.121.234.1', cleanremoteaddr('123.121.234.01 '));
  50. // IPv6.
  51. $this->assertNull(cleanremoteaddr('0:0:0:0:0:0:0:0:0'));
  52. $this->assertNull(cleanremoteaddr('0:0:0:0:0:0:0:abh'));
  53. $this->assertNull(cleanremoteaddr('0:0:0:::0:0:1'));
  54. $this->assertSame('::', cleanremoteaddr('0:0:0:0:0:0:0:0', true));
  55. $this->assertSame('::1:1', cleanremoteaddr('0:0:0:0:0:0:1:1', true));
  56. $this->assertSame('abcd:ef::', cleanremoteaddr('abcd:00ef:0:0:0:0:0:0', true));
  57. $this->assertSame('1::1', cleanremoteaddr('1:0:0:0:0:0:0:1', true));
  58. $this->assertSame('0:0:0:0:0:0:10:1', cleanremoteaddr('::10:1', false));
  59. $this->assertSame('1:1:0:0:0:0:0:0', cleanremoteaddr('01:1::', false));
  60. $this->assertSame('10:0:0:0:0:0:0:10', cleanremoteaddr('10::10', false));
  61. $this->assertSame('::ffff:c0a8:11', cleanremoteaddr('::ffff:192.168.1.1', true));
  62. }
  63. public function test_address_in_subnet() {
  64. // 1: xxx.xxx.xxx.xxx/nn or xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx/nnn (number of bits in net mask).
  65. $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.1/32'));
  66. $this->assertFalse(address_in_subnet('123.121.23.1', '123.121.23.0/32'));
  67. $this->assertTrue(address_in_subnet('10.10.10.100', '123.121.23.45/0'));
  68. $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/24'));
  69. $this->assertFalse(address_in_subnet('123.121.34.1', '123.121.234.0/24'));
  70. $this->assertTrue(address_in_subnet('123.121.234.1', '123.121.234.0/30'));
  71. $this->assertFalse(address_in_subnet('123.121.23.8', '123.121.23.0/30'));
  72. $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128'));
  73. $this->assertFalse(address_in_subnet('bab:baba::baba', 'bab:baba::cece/128'));
  74. $this->assertTrue(address_in_subnet('baba:baba::baba', 'cece:cece::cece/0'));
  75. $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba/128'));
  76. $this->assertTrue(address_in_subnet('baba:baba::00ba', 'baba:baba::/120'));
  77. $this->assertFalse(address_in_subnet('baba:baba::aba', 'baba:baba::/120'));
  78. $this->assertTrue(address_in_subnet('baba::baba:00ba', 'baba::baba:0/112'));
  79. $this->assertFalse(address_in_subnet('baba::aba:00ba', 'baba::baba:0/112'));
  80. $this->assertFalse(address_in_subnet('aba::baba:0000', 'baba::baba:0/112'));
  81. // Fixed input.
  82. $this->assertTrue(address_in_subnet('123.121.23.1 ', ' 123.121.23.0 / 24'));
  83. $this->assertTrue(address_in_subnet('::ffff:10.1.1.1', ' 0:0:0:000:0:ffff:a1:10 / 126'));
  84. // Incorrect input.
  85. $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/-2'));
  86. $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/64'));
  87. $this->assertFalse(address_in_subnet('123.121.234.x', '123.121.234.1/24'));
  88. $this->assertFalse(address_in_subnet('123.121.234.0', '123.121.234.xx/24'));
  89. $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.1/xx0'));
  90. $this->assertFalse(address_in_subnet('::1', '::aa:0/xx0'));
  91. $this->assertFalse(address_in_subnet('::1', '::aa:0/-5'));
  92. $this->assertFalse(address_in_subnet('::1', '::aa:0/130'));
  93. $this->assertFalse(address_in_subnet('x:1', '::aa:0/130'));
  94. $this->assertFalse(address_in_subnet('::1', '::ax:0/130'));
  95. // 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).
  96. $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12-14'));
  97. $this->assertTrue(address_in_subnet('123.121.234.13', '123.121.234.12-14'));
  98. $this->assertTrue(address_in_subnet('123.121.234.14', '123.121.234.12-14'));
  99. $this->assertFalse(address_in_subnet('123.121.234.1', '123.121.234.12-14'));
  100. $this->assertFalse(address_in_subnet('123.121.234.20', '123.121.234.12-14'));
  101. $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.234.12-14'));
  102. $this->assertFalse(address_in_subnet('123.12.234.12', '123.121.234.12-14'));
  103. $this->assertTrue(address_in_subnet('baba:baba::baba', 'baba:baba::baba-babe'));
  104. $this->assertTrue(address_in_subnet('baba:baba::babc', 'baba:baba::baba-babe'));
  105. $this->assertTrue(address_in_subnet('baba:baba::babe', 'baba:baba::baba-babe'));
  106. $this->assertFalse(address_in_subnet('bab:baba::bab0', 'bab:baba::baba-babe'));
  107. $this->assertFalse(address_in_subnet('bab:baba::babf', 'bab:baba::baba-babe'));
  108. $this->assertFalse(address_in_subnet('bab:baba::bfbe', 'bab:baba::baba-babe'));
  109. $this->assertFalse(address_in_subnet('bfb:baba::babe', 'bab:baba::baba-babe'));
  110. // Fixed input.
  111. $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12 - 14 '));
  112. $this->assertTrue(address_in_subnet('bab:baba::babe', 'bab:baba::baba - babe '));
  113. // Incorrect input.
  114. $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-234.14'));
  115. $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12-256'));
  116. $this->assertFalse(address_in_subnet('123.121.234.12', '123.121.234.12--256'));
  117. // 3: xxx.xxx or xxx.xxx. or xxx:xxx:xxxx or xxx:xxx:xxxx. (incomplete address, a bit non-technical ;-).
  118. $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.12'));
  119. $this->assertFalse(address_in_subnet('123.121.23.12', '123.121.23.13'));
  120. $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234.'));
  121. $this->assertTrue(address_in_subnet('123.121.234.12', '123.121.234'));
  122. $this->assertTrue(address_in_subnet('123.121.234.12', '123.121'));
  123. $this->assertTrue(address_in_subnet('123.121.234.12', '123'));
  124. $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234.'));
  125. $this->assertFalse(address_in_subnet('123.121.234.1', '12.121.234'));
  126. $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba::bab'));
  127. $this->assertFalse(address_in_subnet('baba:baba::ba', 'baba:baba::bc'));
  128. $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:baba'));
  129. $this->assertTrue(address_in_subnet('baba:baba::bab', 'baba:'));
  130. $this->assertFalse(address_in_subnet('bab:baba::bab', 'baba:'));
  131. // Multiple subnets.
  132. $this->assertTrue(address_in_subnet('123.121.234.12', '::1/64, 124., 123.121.234.10-30'));
  133. $this->assertTrue(address_in_subnet('124.121.234.12', '::1/64, 124., 123.121.234.10-30'));
  134. $this->assertTrue(address_in_subnet('::2', '::1/64, 124., 123.121.234.10-30'));
  135. $this->assertFalse(address_in_subnet('12.121.234.12', '::1/64, 124., 123.121.234.10-30'));
  136. // Other incorrect input.
  137. $this->assertFalse(address_in_subnet('123.123.123.123', ''));
  138. }
  139. public function test_fix_utf8() {
  140. // Make sure valid data including other types is not changed.
  141. $this->assertSame(null, fix_utf8(null));
  142. $this->assertSame(1, fix_utf8(1));
  143. $this->assertSame(1.1, fix_utf8(1.1));
  144. $this->assertSame(true, fix_utf8(true));
  145. $this->assertSame('', fix_utf8(''));
  146. $this->assertSame('abc', fix_utf8('abc'));
  147. $array = array('do', 're', 'mi');
  148. $this->assertSame($array, fix_utf8($array));
  149. $object = new stdClass();
  150. $object->a = 'aa';
  151. $object->b = 'bb';
  152. $this->assertEquals($object, fix_utf8($object));
  153. // valid utf8 string
  154. $this->assertSame("žlutý koníček přeskočil potůček \n\t\r", fix_utf8("žlutý koníček přeskočil potůček \n\t\r\0"));
  155. // Invalid utf8 string.
  156. $this->assertSame('aš', fix_utf8('a'.chr(130).'š'), 'This fails with buggy iconv() when mbstring extenstion is not available as fallback.');
  157. }
  158. public function test_optional_param() {
  159. global $CFG;
  160. $_POST['username'] = 'post_user';
  161. $_GET['username'] = 'get_user';
  162. $this->assertSame($_POST['username'], optional_param('username', 'default_user', PARAM_RAW));
  163. unset($_POST['username']);
  164. $this->assertSame($_GET['username'], optional_param('username', 'default_user', PARAM_RAW));
  165. unset($_GET['username']);
  166. $this->assertSame('default_user', optional_param('username', 'default_user', PARAM_RAW));
  167. // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
  168. $_POST['username'] = 'post_user';
  169. try {
  170. optional_param('username', 'default_user', null);
  171. $this->fail('coding_exception expected');
  172. } catch (moodle_exception $ex) {
  173. $this->assertInstanceOf('coding_exception', $ex);
  174. }
  175. try {
  176. @optional_param('username', 'default_user');
  177. $this->fail('coding_exception expected');
  178. } catch (moodle_exception $ex) {
  179. $this->assertInstanceOf('coding_exception', $ex);
  180. }
  181. try {
  182. @optional_param('username');
  183. $this->fail('coding_exception expected');
  184. } catch (moodle_exception $ex) {
  185. $this->assertInstanceOf('coding_exception', $ex);
  186. }
  187. try {
  188. optional_param('', 'default_user', PARAM_RAW);
  189. $this->fail('coding_exception expected');
  190. } catch (moodle_exception $ex) {
  191. $this->assertInstanceOf('coding_exception', $ex);
  192. }
  193. // Make sure warning is displayed if array submitted - TODO: throw exception in Moodle 2.3.
  194. $_POST['username'] = array('a'=>'a');
  195. $this->assertSame($_POST['username'], optional_param('username', 'default_user', PARAM_RAW));
  196. $this->assertDebuggingCalled();
  197. }
  198. public function test_optional_param_array() {
  199. global $CFG;
  200. $_POST['username'] = array('a'=>'post_user');
  201. $_GET['username'] = array('a'=>'get_user');
  202. $this->assertSame($_POST['username'], optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
  203. unset($_POST['username']);
  204. $this->assertSame($_GET['username'], optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
  205. unset($_GET['username']);
  206. $this->assertSame(array('a'=>'default_user'), optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
  207. // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
  208. $_POST['username'] = array('a'=>'post_user');
  209. try {
  210. optional_param_array('username', array('a'=>'default_user'), null);
  211. $this->fail('coding_exception expected');
  212. } catch (moodle_exception $ex) {
  213. $this->assertInstanceOf('coding_exception', $ex);
  214. }
  215. try {
  216. @optional_param_array('username', array('a'=>'default_user'));
  217. $this->fail('coding_exception expected');
  218. } catch (moodle_exception $ex) {
  219. $this->assertInstanceOf('coding_exception', $ex);
  220. }
  221. try {
  222. @optional_param_array('username');
  223. $this->fail('coding_exception expected');
  224. } catch (moodle_exception $ex) {
  225. $this->assertInstanceOf('coding_exception', $ex);
  226. }
  227. try {
  228. optional_param_array('', array('a'=>'default_user'), PARAM_RAW);
  229. $this->fail('coding_exception expected');
  230. } catch (moodle_exception $ex) {
  231. $this->assertInstanceOf('coding_exception', $ex);
  232. }
  233. // Do not allow nested arrays.
  234. try {
  235. $_POST['username'] = array('a'=>array('b'=>'post_user'));
  236. optional_param_array('username', array('a'=>'default_user'), PARAM_RAW);
  237. $this->fail('coding_exception expected');
  238. } catch (coding_exception $ex) {
  239. $this->assertTrue(true);
  240. }
  241. // Do not allow non-arrays.
  242. $_POST['username'] = 'post_user';
  243. $this->assertSame(array('a'=>'default_user'), optional_param_array('username', array('a'=>'default_user'), PARAM_RAW));
  244. $this->assertDebuggingCalled();
  245. // Make sure array keys are sanitised.
  246. $_POST['username'] = array('abc123_;-/*-+ '=>'arrggh', 'a1_-'=>'post_user');
  247. $this->assertSame(array('a1_-'=>'post_user'), optional_param_array('username', array(), PARAM_RAW));
  248. $this->assertDebuggingCalled();
  249. }
  250. public function test_required_param() {
  251. $_POST['username'] = 'post_user';
  252. $_GET['username'] = 'get_user';
  253. $this->assertSame('post_user', required_param('username', PARAM_RAW));
  254. unset($_POST['username']);
  255. $this->assertSame('get_user', required_param('username', PARAM_RAW));
  256. unset($_GET['username']);
  257. try {
  258. $this->assertSame('default_user', required_param('username', PARAM_RAW));
  259. $this->fail('moodle_exception expected');
  260. } catch (moodle_exception $ex) {
  261. $this->assertInstanceOf('moodle_exception', $ex);
  262. }
  263. // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
  264. $_POST['username'] = 'post_user';
  265. try {
  266. @required_param('username');
  267. $this->fail('coding_exception expected');
  268. } catch (moodle_exception $ex) {
  269. $this->assertInstanceOf('coding_exception', $ex);
  270. }
  271. try {
  272. required_param('username', '');
  273. $this->fail('coding_exception expected');
  274. } catch (moodle_exception $ex) {
  275. $this->assertInstanceOf('coding_exception', $ex);
  276. }
  277. try {
  278. required_param('', PARAM_RAW);
  279. $this->fail('coding_exception expected');
  280. } catch (moodle_exception $ex) {
  281. $this->assertInstanceOf('coding_exception', $ex);
  282. }
  283. // Make sure warning is displayed if array submitted - TODO: throw exception in Moodle 2.3.
  284. $_POST['username'] = array('a'=>'a');
  285. $this->assertSame($_POST['username'], required_param('username', PARAM_RAW));
  286. $this->assertDebuggingCalled();
  287. }
  288. public function test_required_param_array() {
  289. global $CFG;
  290. $_POST['username'] = array('a'=>'post_user');
  291. $_GET['username'] = array('a'=>'get_user');
  292. $this->assertSame($_POST['username'], required_param_array('username', PARAM_RAW));
  293. unset($_POST['username']);
  294. $this->assertSame($_GET['username'], required_param_array('username', PARAM_RAW));
  295. // Make sure exception is triggered when some params are missing, hide error notices here - new in 2.2.
  296. $_POST['username'] = array('a'=>'post_user');
  297. try {
  298. required_param_array('username', null);
  299. $this->fail('coding_exception expected');
  300. } catch (moodle_exception $ex) {
  301. $this->assertInstanceOf('coding_exception', $ex);
  302. }
  303. try {
  304. @required_param_array('username');
  305. $this->fail('coding_exception expected');
  306. } catch (moodle_exception $ex) {
  307. $this->assertInstanceOf('coding_exception', $ex);
  308. }
  309. try {
  310. required_param_array('', PARAM_RAW);
  311. $this->fail('coding_exception expected');
  312. } catch (moodle_exception $ex) {
  313. $this->assertInstanceOf('coding_exception', $ex);
  314. }
  315. // Do not allow nested arrays.
  316. try {
  317. $_POST['username'] = array('a'=>array('b'=>'post_user'));
  318. required_param_array('username', PARAM_RAW);
  319. $this->fail('coding_exception expected');
  320. } catch (moodle_exception $ex) {
  321. $this->assertInstanceOf('coding_exception', $ex);
  322. }
  323. // Do not allow non-arrays.
  324. try {
  325. $_POST['username'] = 'post_user';
  326. required_param_array('username', PARAM_RAW);
  327. $this->fail('moodle_exception expected');
  328. } catch (moodle_exception $ex) {
  329. $this->assertInstanceOf('moodle_exception', $ex);
  330. }
  331. // Make sure array keys are sanitised.
  332. $_POST['username'] = array('abc123_;-/*-+ '=>'arrggh', 'a1_-'=>'post_user');
  333. $this->assertSame(array('a1_-'=>'post_user'), required_param_array('username', PARAM_RAW));
  334. $this->assertDebuggingCalled();
  335. }
  336. public function test_clean_param() {
  337. // Forbid objects and arrays.
  338. try {
  339. clean_param(array('x', 'y'), PARAM_RAW);
  340. $this->fail('coding_exception expected');
  341. } catch (moodle_exception $ex) {
  342. $this->assertInstanceOf('coding_exception', $ex);
  343. }
  344. try {
  345. $param = new stdClass();
  346. $param->id = 1;
  347. clean_param($param, PARAM_RAW);
  348. $this->fail('coding_exception expected');
  349. } catch (moodle_exception $ex) {
  350. $this->assertInstanceOf('coding_exception', $ex);
  351. }
  352. // Require correct type.
  353. try {
  354. clean_param('x', 'xxxxxx');
  355. $this->fail('moodle_exception expected');
  356. } catch (moodle_exception $ex) {
  357. $this->assertInstanceOf('moodle_exception', $ex);
  358. }
  359. try {
  360. @clean_param('x');
  361. $this->fail('moodle_exception expected');
  362. } catch (moodle_exception $ex) {
  363. $this->assertInstanceOf('moodle_exception', $ex);
  364. }
  365. }
  366. public function test_clean_param_array() {
  367. $this->assertSame(array(), clean_param_array(null, PARAM_RAW));
  368. $this->assertSame(array('a', 'b'), clean_param_array(array('a', 'b'), PARAM_RAW));
  369. $this->assertSame(array('a', array('b')), clean_param_array(array('a', array('b')), PARAM_RAW, true));
  370. // Require correct type.
  371. try {
  372. clean_param_array(array('x'), 'xxxxxx');
  373. $this->fail('moodle_exception expected');
  374. } catch (moodle_exception $ex) {
  375. $this->assertInstanceOf('moodle_exception', $ex);
  376. }
  377. try {
  378. @clean_param_array(array('x'));
  379. $this->fail('moodle_exception expected');
  380. } catch (moodle_exception $ex) {
  381. $this->assertInstanceOf('moodle_exception', $ex);
  382. }
  383. try {
  384. clean_param_array(array('x', array('y')), PARAM_RAW);
  385. $this->fail('coding_exception expected');
  386. } catch (moodle_exception $ex) {
  387. $this->assertInstanceOf('coding_exception', $ex);
  388. }
  389. // Test recursive.
  390. }
  391. public function test_clean_param_raw() {
  392. $this->assertSame(
  393. '#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)',
  394. clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_RAW));
  395. }
  396. public function test_clean_param_trim() {
  397. $this->assertSame('Frog toad', clean_param(" Frog toad \r\n ", PARAM_RAW_TRIMMED));
  398. }
  399. public function test_clean_param_clean() {
  400. // PARAM_CLEAN is an ugly hack, do not use in new code (skodak),
  401. // instead use more specific type, or submit sothing that can be verified properly.
  402. $this->assertSame('xx', clean_param('xx<script>', PARAM_CLEAN));
  403. }
  404. public function test_clean_param_alpha() {
  405. $this->assertSame('DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHA));
  406. }
  407. public function test_clean_param_alphanum() {
  408. $this->assertSame('978942897DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHANUM));
  409. }
  410. public function test_clean_param_alphaext() {
  411. $this->assertSame('DSFMOSDJ', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_ALPHAEXT));
  412. }
  413. public function test_clean_param_sequence() {
  414. $this->assertSame(',9789,42897', clean_param('#()*#,9789\'".,<42897></?$(*DSFMO#$*)(SDJ)($*)', PARAM_SEQUENCE));
  415. }
  416. public function test_clean_param_component() {
  417. // Please note the cleaning of component names is very strict, no guessing here.
  418. $this->assertSame('mod_forum', clean_param('mod_forum', PARAM_COMPONENT));
  419. $this->assertSame('block_online_users', clean_param('block_online_users', PARAM_COMPONENT));
  420. $this->assertSame('block_blond_online_users', clean_param('block_blond_online_users', PARAM_COMPONENT));
  421. $this->assertSame('mod_something2', clean_param('mod_something2', PARAM_COMPONENT));
  422. $this->assertSame('forum', clean_param('forum', PARAM_COMPONENT));
  423. $this->assertSame('user', clean_param('user', PARAM_COMPONENT));
  424. $this->assertSame('rating', clean_param('rating', PARAM_COMPONENT));
  425. $this->assertSame('feedback360', clean_param('feedback360', PARAM_COMPONENT));
  426. $this->assertSame('mod_feedback360', clean_param('mod_feedback360', PARAM_COMPONENT));
  427. $this->assertSame('', clean_param('mod_2something', PARAM_COMPONENT));
  428. $this->assertSame('', clean_param('2mod_something', PARAM_COMPONENT));
  429. $this->assertSame('', clean_param('mod_something_xx', PARAM_COMPONENT));
  430. $this->assertSame('', clean_param('auth_something__xx', PARAM_COMPONENT));
  431. $this->assertSame('', clean_param('mod_Something', PARAM_COMPONENT));
  432. $this->assertSame('', clean_param('mod_somethíng', PARAM_COMPONENT));
  433. $this->assertSame('', clean_param('mod__something', PARAM_COMPONENT));
  434. $this->assertSame('', clean_param('auth_xx-yy', PARAM_COMPONENT));
  435. $this->assertSame('', clean_param('_auth_xx', PARAM_COMPONENT));
  436. $this->assertSame('', clean_param('a2uth_xx', PARAM_COMPONENT));
  437. $this->assertSame('', clean_param('auth_xx_', PARAM_COMPONENT));
  438. $this->assertSame('', clean_param('auth_xx.old', PARAM_COMPONENT));
  439. $this->assertSame('', clean_param('_user', PARAM_COMPONENT));
  440. $this->assertSame('', clean_param('2rating', PARAM_COMPONENT));
  441. $this->assertSame('', clean_param('user_', PARAM_COMPONENT));
  442. }
  443. public function test_is_valid_plugin_name() {
  444. $this->assertTrue(is_valid_plugin_name('forum'));
  445. $this->assertTrue(is_valid_plugin_name('forum2'));
  446. $this->assertTrue(is_valid_plugin_name('feedback360'));
  447. $this->assertTrue(is_valid_plugin_name('online_users'));
  448. $this->assertTrue(is_valid_plugin_name('blond_online_users'));
  449. $this->assertFalse(is_valid_plugin_name('online__users'));
  450. $this->assertFalse(is_valid_plugin_name('forum '));
  451. $this->assertFalse(is_valid_plugin_name('forum.old'));
  452. $this->assertFalse(is_valid_plugin_name('xx-yy'));
  453. $this->assertFalse(is_valid_plugin_name('2xx'));
  454. $this->assertFalse(is_valid_plugin_name('Xx'));
  455. $this->assertFalse(is_valid_plugin_name('_xx'));
  456. $this->assertFalse(is_valid_plugin_name('xx_'));
  457. }
  458. public function test_clean_param_plugin() {
  459. // Please note the cleaning of plugin names is very strict, no guessing here.
  460. $this->assertSame('forum', clean_param('forum', PARAM_PLUGIN));
  461. $this->assertSame('forum2', clean_param('forum2', PARAM_PLUGIN));
  462. $this->assertSame('feedback360', clean_param('feedback360', PARAM_PLUGIN));
  463. $this->assertSame('online_users', clean_param('online_users', PARAM_PLUGIN));
  464. $this->assertSame('blond_online_users', clean_param('blond_online_users', PARAM_PLUGIN));
  465. $this->assertSame('', clean_param('online__users', PARAM_PLUGIN));
  466. $this->assertSame('', clean_param('forum ', PARAM_PLUGIN));
  467. $this->assertSame('', clean_param('forum.old', PARAM_PLUGIN));
  468. $this->assertSame('', clean_param('xx-yy', PARAM_PLUGIN));
  469. $this->assertSame('', clean_param('2xx', PARAM_PLUGIN));
  470. $this->assertSame('', clean_param('Xx', PARAM_PLUGIN));
  471. $this->assertSame('', clean_param('_xx', PARAM_PLUGIN));
  472. $this->assertSame('', clean_param('xx_', PARAM_PLUGIN));
  473. }
  474. public function test_clean_param_area() {
  475. // Please note the cleaning of area names is very strict, no guessing here.
  476. $this->assertSame('something', clean_param('something', PARAM_AREA));
  477. $this->assertSame('something2', clean_param('something2', PARAM_AREA));
  478. $this->assertSame('some_thing', clean_param('some_thing', PARAM_AREA));
  479. $this->assertSame('some_thing_xx', clean_param('some_thing_xx', PARAM_AREA));
  480. $this->assertSame('feedback360', clean_param('feedback360', PARAM_AREA));
  481. $this->assertSame('', clean_param('_something', PARAM_AREA));
  482. $this->assertSame('', clean_param('something_', PARAM_AREA));
  483. $this->assertSame('', clean_param('2something', PARAM_AREA));
  484. $this->assertSame('', clean_param('Something', PARAM_AREA));
  485. $this->assertSame('', clean_param('some-thing', PARAM_AREA));
  486. $this->assertSame('', clean_param('somethííng', PARAM_AREA));
  487. $this->assertSame('', clean_param('something.x', PARAM_AREA));
  488. }
  489. public function test_clean_param_text() {
  490. $this->assertSame(PARAM_TEXT, PARAM_MULTILANG);
  491. // Standard.
  492. $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));
  493. $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));
  494. $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));
  495. // Malformed.
  496. $this->assertSame('<span lang="en" class="multilang">aa</span>', clean_param('<span lang="en" class="multilang">aa</span>', PARAM_TEXT));
  497. $this->assertSame('aa', clean_param('<span lang="en" class="nothing" class="multilang">aa</span>', PARAM_TEXT));
  498. $this->assertSame('aa', clean_param('<lang lang="en" class="multilang">aa</lang>', PARAM_TEXT));
  499. $this->assertSame('aa', clean_param('<lang lang="en!!">aa</lang>', PARAM_TEXT));
  500. $this->assertSame('aa', clean_param('<span lang="en==" class="multilang">aa</span>', PARAM_TEXT));
  501. $this->assertSame('abc', clean_param('a<em>b</em>c', PARAM_TEXT));
  502. $this->assertSame('a>c>', clean_param('a><xx >c>', PARAM_TEXT)); // Standard strip_tags() behaviour.
  503. $this->assertSame('a', clean_param('a<b', PARAM_TEXT));
  504. $this->assertSame('a>b', clean_param('a>b', PARAM_TEXT));
  505. $this->assertSame('<lang lang="en">a>a</lang>', clean_param('<lang lang="en">a>a</lang>', PARAM_TEXT)); // Standard strip_tags() behaviour.
  506. $this->assertSame('a', clean_param('<lang lang="en">a<a</lang>', PARAM_TEXT));
  507. $this->assertSame('<lang lang="en">aa</lang>', clean_param('<lang lang="en">a<br>a</lang>', PARAM_TEXT));
  508. }
  509. public function test_clean_param_url() {
  510. // Test PARAM_URL and PARAM_LOCALURL a bit.
  511. $this->assertSame('http://google.com/', clean_param('http://google.com/', PARAM_URL));
  512. $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));
  513. $this->assertSame('http://localhost/', clean_param('http://localhost/', PARAM_URL));
  514. $this->assertSame('http://0.255.1.1/numericip.php', clean_param('http://0.255.1.1/numericip.php', PARAM_URL));
  515. $this->assertSame('/just/a/path', clean_param('/just/a/path', PARAM_URL));
  516. $this->assertSame('', clean_param('funny:thing', PARAM_URL));
  517. }
  518. public function test_clean_param_localurl() {
  519. global $CFG;
  520. $this->assertSame('', clean_param('http://google.com/', PARAM_LOCALURL));
  521. $this->assertSame('', clean_param('http://some.very.long.and.silly.domain/with/a/path/', PARAM_LOCALURL));
  522. $this->assertSame(clean_param($CFG->wwwroot, PARAM_LOCALURL), $CFG->wwwroot);
  523. $this->assertSame('/just/a/path', clean_param('/just/a/path', PARAM_LOCALURL));
  524. $this->assertSame('', clean_param('funny:thing', PARAM_LOCALURL));
  525. $this->assertSame('course/view.php?id=3', clean_param('course/view.php?id=3', PARAM_LOCALURL));
  526. }
  527. public function test_clean_param_file() {
  528. $this->assertSame('correctfile.txt', clean_param('correctfile.txt', PARAM_FILE));
  529. $this->assertSame('badfile.txt', clean_param('b\'a<d`\\/fi:l>e.t"x|t', PARAM_FILE));
  530. $this->assertSame('..parentdirfile.txt', clean_param('../parentdirfile.txt', PARAM_FILE));
  531. $this->assertSame('....grandparentdirfile.txt', clean_param('../../grandparentdirfile.txt', PARAM_FILE));
  532. $this->assertSame('..winparentdirfile.txt', clean_param('..\winparentdirfile.txt', PARAM_FILE));
  533. $this->assertSame('....wingrandparentdir.txt', clean_param('..\..\wingrandparentdir.txt', PARAM_FILE));
  534. $this->assertSame('myfile.a.b.txt', clean_param('myfile.a.b.txt', PARAM_FILE));
  535. $this->assertSame('myfile..a..b.txt', clean_param('myfile..a..b.txt', PARAM_FILE));
  536. $this->assertSame('myfile.a..b...txt', clean_param('myfile.a..b...txt', PARAM_FILE));
  537. $this->assertSame('myfile.a.txt', clean_param('myfile.a.txt', PARAM_FILE));
  538. $this->assertSame('myfile...txt', clean_param('myfile...txt', PARAM_FILE));
  539. $this->assertSame('...jpg', clean_param('...jpg', PARAM_FILE));
  540. $this->assertSame('.a.b.', clean_param('.a.b.', PARAM_FILE));
  541. $this->assertSame('', clean_param('.', PARAM_FILE));
  542. $this->assertSame('', clean_param('..', PARAM_FILE));
  543. $this->assertSame('...', clean_param('...', PARAM_FILE));
  544. $this->assertSame('. . . .', clean_param('. . . .', PARAM_FILE));
  545. $this->assertSame('dontrtrim.me. .. .. . ', clean_param('dontrtrim.me. .. .. . ', PARAM_FILE));
  546. $this->assertSame(' . .dontltrim.me', clean_param(' . .dontltrim.me', PARAM_FILE));
  547. $this->assertSame('here is a tab.txt', clean_param("here is a tab\t.txt", PARAM_FILE));
  548. $this->assertSame('here is a linebreak.txt', clean_param("here is a line\r\nbreak.txt", PARAM_FILE));
  549. // The following behaviours have been maintained although they seem a little odd.
  550. $this->assertSame('funnything', clean_param('funny:thing', PARAM_FILE));
  551. $this->assertSame('.currentdirfile.txt', clean_param('./currentdirfile.txt', PARAM_FILE));
  552. $this->assertSame('ctempwindowsfile.txt', clean_param('c:\temp\windowsfile.txt', PARAM_FILE));
  553. $this->assertSame('homeuserlinuxfile.txt', clean_param('/home/user/linuxfile.txt', PARAM_FILE));
  554. $this->assertSame('~myfile.txt', clean_param('~/myfile.txt', PARAM_FILE));
  555. }
  556. public function test_clean_param_path() {
  557. $this->assertSame('correctfile.txt', clean_param('correctfile.txt', PARAM_PATH));
  558. $this->assertSame('bad/file.txt', clean_param('b\'a<d`\\/fi:l>e.t"x|t', PARAM_PATH));
  559. $this->assertSame('/parentdirfile.txt', clean_param('../parentdirfile.txt', PARAM_PATH));
  560. $this->assertSame('/grandparentdirfile.txt', clean_param('../../grandparentdirfile.txt', PARAM_PATH));
  561. $this->assertSame('/winparentdirfile.txt', clean_param('..\winparentdirfile.txt', PARAM_PATH));
  562. $this->assertSame('/wingrandparentdir.txt', clean_param('..\..\wingrandparentdir.txt', PARAM_PATH));
  563. $this->assertSame('funnything', clean_param('funny:thing', PARAM_PATH));
  564. $this->assertSame('./here', clean_param('./././here', PARAM_PATH));
  565. $this->assertSame('./currentdirfile.txt', clean_param('./currentdirfile.txt', PARAM_PATH));
  566. $this->assertSame('c/temp/windowsfile.txt', clean_param('c:\temp\windowsfile.txt', PARAM_PATH));
  567. $this->assertSame('/home/user/linuxfile.txt', clean_param('/home/user/linuxfile.txt', PARAM_PATH));
  568. $this->assertSame('/home../user ./.linuxfile.txt', clean_param('/home../user ./.linuxfile.txt', PARAM_PATH));
  569. $this->assertSame('~/myfile.txt', clean_param('~/myfile.txt', PARAM_PATH));
  570. $this->assertSame('~/myfile.txt', clean_param('~/../myfile.txt', PARAM_PATH));
  571. $this->assertSame('/..b../.../myfile.txt', clean_param('/..b../.../myfile.txt', PARAM_PATH));
  572. $this->assertSame('..b../.../myfile.txt', clean_param('..b../.../myfile.txt', PARAM_PATH));
  573. $this->assertSame('/super/slashes/', clean_param('/super//slashes///', PARAM_PATH));
  574. }
  575. public function test_clean_param_username() {
  576. global $CFG;
  577. $currentstatus = $CFG->extendedusernamechars;
  578. // Run tests with extended character == false;.
  579. $CFG->extendedusernamechars = false;
  580. $this->assertSame('johndoe123', clean_param('johndoe123', PARAM_USERNAME) );
  581. $this->assertSame('john.doe', clean_param('john.doe', PARAM_USERNAME));
  582. $this->assertSame('john-doe', clean_param('john-doe', PARAM_USERNAME));
  583. $this->assertSame('john-doe', clean_param('john- doe', PARAM_USERNAME));
  584. $this->assertSame('john_doe', clean_param('john_doe', PARAM_USERNAME));
  585. $this->assertSame('john@doe', clean_param('john@doe', PARAM_USERNAME));
  586. $this->assertSame('johndoe', clean_param('john~doe', PARAM_USERNAME));
  587. $this->assertSame('johndoe', clean_param('john´doe', PARAM_USERNAME));
  588. $this->assertSame(clean_param('john#$%&() ', PARAM_USERNAME), 'john');
  589. $this->assertSame('johnd', clean_param('JOHNdóé ', PARAM_USERNAME));
  590. $this->assertSame(clean_param('john.,:;-_/|\ñÑ[]A_X-,D {} ~!@#$%^&*()_+ ?><[] ščřžžý ?ýáž?žý??šdoe ', PARAM_USERNAME), 'john.-_a_x-d@_doe');
  591. // Test success condition, if extendedusernamechars == ENABLE;.
  592. $CFG->extendedusernamechars = true;
  593. $this->assertSame('john_doe', clean_param('john_doe', PARAM_USERNAME));
  594. $this->assertSame('john@doe', clean_param('john@doe', PARAM_USERNAME));
  595. $this->assertSame(clean_param('john# $%&()+_^', PARAM_USERNAME), 'john#$%&()+_^');
  596. $this->assertSame('john~doe', clean_param('john~doe', PARAM_USERNAME));
  597. $this->assertSame('john´doe', clean_param('joHN´doe', PARAM_USERNAME));
  598. $this->assertSame('johndoe', clean_param('johnDOE', PARAM_USERNAME));
  599. $this->assertSame('johndóé', clean_param('johndóé ', PARAM_USERNAME));
  600. $CFG->extendedusernamechars = $currentstatus;
  601. }
  602. public function test_clean_param_stringid() {
  603. // Test string identifiers validation.
  604. // Valid strings.
  605. $this->assertSame('validstring', clean_param('validstring', PARAM_STRINGID));
  606. $this->assertSame('mod/foobar:valid_capability', clean_param('mod/foobar:valid_capability', PARAM_STRINGID));
  607. $this->assertSame('CZ', clean_param('CZ', PARAM_STRINGID));
  608. $this->assertSame('application/vnd.ms-powerpoint', clean_param('application/vnd.ms-powerpoint', PARAM_STRINGID));
  609. $this->assertSame('grade2', clean_param('grade2', PARAM_STRINGID));
  610. // Invalid strings.
  611. $this->assertSame('', clean_param('trailing ', PARAM_STRINGID));
  612. $this->assertSame('', clean_param('space bar', PARAM_STRINGID));
  613. $this->assertSame('', clean_param('0numeric', PARAM_STRINGID));
  614. $this->assertSame('', clean_param('*', PARAM_STRINGID));
  615. $this->assertSame('', clean_param(' ', PARAM_STRINGID));
  616. }
  617. public function test_clean_param_timezone() {
  618. // Test timezone validation.
  619. $testvalues = array (
  620. 'America/Jamaica' => 'America/Jamaica',
  621. 'America/Argentina/Cordoba' => 'America/Argentina/Cordoba',
  622. 'America/Port-au-Prince' => 'America/Port-au-Prince',
  623. 'America/Argentina/Buenos_Aires' => 'America/Argentina/Buenos_Aires',
  624. 'PST8PDT' => 'PST8PDT',
  625. 'Wrong.Value' => '',
  626. 'Wrong/.Value' => '',
  627. 'Wrong(Value)' => '',
  628. '0' => '0',
  629. '0.0' => '0.0',
  630. '0.5' => '0.5',
  631. '9.0' => '9.0',
  632. '-9.0' => '-9.0',
  633. '+9.0' => '+9.0',
  634. '9.5' => '9.5',
  635. '-9.5' => '-9.5',
  636. '+9.5' => '+9.5',
  637. '12.0' => '12.0',
  638. '-12.0' => '-12.0',
  639. '+12.0' => '+12.0',
  640. '12.5' => '12.5',
  641. '-12.5' => '-12.5',
  642. '+12.5' => '+12.5',
  643. '13.0' => '13.0',
  644. '-13.0' => '-13.0',
  645. '+13.0' => '+13.0',
  646. '13.5' => '',
  647. '+13.5' => '',
  648. '-13.5' => '',
  649. '0.2' => '');
  650. foreach ($testvalues as $testvalue => $expectedvalue) {
  651. $actualvalue = clean_param($testvalue, PARAM_TIMEZONE);
  652. $this->assertEquals($expectedvalue, $actualvalue);
  653. }
  654. }
  655. public function test_validate_param() {
  656. try {
  657. $param = validate_param('11a', PARAM_INT);
  658. $this->fail('invalid_parameter_exception expected');
  659. } catch (moodle_exception $ex) {
  660. $this->assertInstanceOf('invalid_parameter_exception', $ex);
  661. }
  662. $param = validate_param('11', PARAM_INT);
  663. $this->assertSame(11, $param);
  664. try {
  665. $param = validate_param(null, PARAM_INT, false);
  666. $this->fail('invalid_parameter_exception expected');
  667. } catch (moodle_exception $ex) {
  668. $this->assertInstanceOf('invalid_parameter_exception', $ex);
  669. }
  670. $param = validate_param(null, PARAM_INT, true);
  671. $this->assertSame(null, $param);
  672. try {
  673. $param = validate_param(array(), PARAM_INT);
  674. $this->fail('invalid_parameter_exception expected');
  675. } catch (moodle_exception $ex) {
  676. $this->assertInstanceOf('invalid_parameter_exception', $ex);
  677. }
  678. try {
  679. $param = validate_param(new stdClass, PARAM_INT);
  680. $this->fail('invalid_parameter_exception expected');
  681. } catch (moodle_exception $ex) {
  682. $this->assertInstanceOf('invalid_parameter_exception', $ex);
  683. }
  684. $param = validate_param('1.0', PARAM_FLOAT);
  685. $this->assertSame(1.0, $param);
  686. // Make sure valid floats do not cause exception.
  687. validate_param(1.0, PARAM_FLOAT);
  688. validate_param(10, PARAM_FLOAT);
  689. validate_param('0', PARAM_FLOAT);
  690. validate_param('119813454.545464564564546564545646556564465465456465465465645645465645645645', PARAM_FLOAT);
  691. validate_param('011.1', PARAM_FLOAT);
  692. validate_param('11', PARAM_FLOAT);
  693. validate_param('+.1', PARAM_FLOAT);
  694. validate_param('-.1', PARAM_FLOAT);
  695. validate_param('1e10', PARAM_FLOAT);
  696. validate_param('.1e+10', PARAM_FLOAT);
  697. validate_param('1E-1', PARAM_FLOAT);
  698. try {
  699. $param = validate_param('1,2', PARAM_FLOAT);
  700. $this->fail('invalid_parameter_exception expected');
  701. } catch (moodle_exception $ex) {
  702. $this->assertInstanceOf('invalid_parameter_exception', $ex);
  703. }
  704. try {
  705. $param = validate_param('', PARAM_FLOAT);
  706. $this->fail('invalid_parameter_exception expected');
  707. } catch (moodle_exception $ex) {
  708. $this->assertInstanceOf('invalid_parameter_exception', $ex);
  709. }
  710. try {
  711. $param = validate_param('.', PARAM_FLOAT);
  712. $this->fail('invalid_parameter_exception expected');
  713. } catch (moodle_exception $ex) {
  714. $this->assertInstanceOf('invalid_parameter_exception', $ex);
  715. }
  716. try {
  717. $param = validate_param('e10', PARAM_FLOAT);
  718. $this->fail('invalid_parameter_exception expected');
  719. } catch (moodle_exception $ex) {
  720. $this->assertInstanceOf('invalid_parameter_exception', $ex);
  721. }
  722. try {
  723. $param = validate_param('abc', PARAM_FLOAT);
  724. $this->fail('invalid_parameter_exception expected');
  725. } catch (moodle_exception $ex) {
  726. $this->assertInstanceOf('invalid_parameter_exception', $ex);
  727. }
  728. }
  729. public function test_shorten_text_no_tags_already_short_enough() {
  730. // ......12345678901234567890123456.
  731. $text = "short text already no tags";
  732. $this->assertSame($text, shorten_text($text));
  733. }
  734. public function test_shorten_text_with_tags_already_short_enough() {
  735. // .........123456...7890....12345678.......901234567.
  736. $text = "<p>short <b>text</b> already</p><p>with tags</p>";
  737. $this->assertSame($text, shorten_text($text));
  738. }
  739. public function test_shorten_text_no_tags_needs_shortening() {
  740. // Default truncation is after 30 chars, but allowing 3 for the final '...'.
  741. // ......12345678901234567890123456789023456789012345678901234.
  742. $text = "long text without any tags blah de blah blah blah what";
  743. $this->assertSame('long text without any tags ...', shorten_text($text));
  744. }
  745. public function test_shorten_text_with_tags_needs_shortening() {
  746. // .......................................123456789012345678901234567890...
  747. $text = "<div class='frog'><p><blockquote>Long text with tags that will ".
  748. "be chopped off but <b>should be added back again</b></blockquote></p></div>";
  749. $this->assertEquals("<div class='frog'><p><blockquote>Long text with " .
  750. "tags that ...</blockquote></p></div>", shorten_text($text));
  751. }
  752. public function test_shorten_text_with_entities() {
  753. // Remember to allow 3 chars for the final '...'.
  754. // ......123456789012345678901234567_____890...
  755. $text = "some text which shouldn't &nbsp; break there";
  756. $this->assertSame("some text which shouldn't &nbsp; ...", shorten_text($text, 31));
  757. $this->assertSame("some text which shouldn't &nbsp;...", shorten_text($text, 30));
  758. $this->assertSame("some text which shouldn't ...", shorten_text($text, 29));
  759. }
  760. public function test_shorten_text_known_tricky_case() {
  761. // This case caused a bug up to 1.9.5
  762. // ..........123456789012345678901234567890123456789.....0_____1___2___...
  763. $text = "<h3>standard 'break-out' sub groups in TGs?</h3>&nbsp;&lt;&lt;There are several";
  764. $this->assertSame("<h3>standard 'break-out' sub groups in ...</h3>",
  765. shorten_text($text, 41));
  766. $this->assertSame("<h3>standard 'break-out' sub groups in TGs?...</h3>",
  767. shorten_text($text, 42));
  768. $this->assertSame("<h3>standard 'break-out' sub groups in TGs?</h3>&nbsp;...",
  769. shorten_text($text, 43));
  770. }
  771. public function test_shorten_text_no_spaces() {
  772. // ..........123456789.
  773. $text = "<h1>123456789</h1>"; // A string with no convenient breaks.
  774. $this->assertSame("<h1>12345...</h1>", shorten_text($text, 8));
  775. }
  776. public function test_shorten_text_utf8_european() {
  777. // Text without tags.
  778. // ......123456789012345678901234567.
  779. $text = "Žluťoučký koníček přeskočil";
  780. $this->assertSame($text, shorten_text($text)); // 30 chars by default.
  781. $this->assertSame("Žluťoučký koníče...", shorten_text($text, 19, true));
  782. $this->assertSame("Žluťoučký ...", shorten_text($text, 19, false));
  783. // And try it with 2-less (that are, in bytes, the middle of a sequence).
  784. $this->assertSame("Žluťoučký koní...", shorten_text($text, 17, true));
  785. $this->assertSame("Žluťoučký ...", shorten_text($text, 17, false));
  786. // .........123456789012345678...901234567....89012345.
  787. $text = "<p>Žluťoučký koníček <b>přeskočil</b> potůček</p>";
  788. $this->assertSame($text, shorten_text($text, 60));
  789. $this->assertSame("<p>Žluťoučký koníček ...</p>", shorten_text($text, 21));
  790. $this->assertSame("<p>Žluťoučký koníče...</p>", shorten_text($text, 19, true));
  791. $this->assertSame("<p>Žluťoučký ...</p>", shorten_text($text, 19, false));
  792. // And try it with 2 fewer (that are, in bytes, the middle of a sequence).
  793. $this->assertSame("<p>Žluťoučký koní...</p>", shorten_text($text, 17, true));
  794. $this->assertSame("<p>Žluťoučký ...</p>", shorten_text($text, 17, false));
  795. // And try over one tag (start/end), it does proper text len.
  796. $this->assertSame("<p>Žluťoučký koníček <b>př...</b></p>", shorten_text($text, 23, true));
  797. $this->assertSame("<p>Žluťoučký koníček <b>přeskočil</b> pot...</p>", shorten_text($text, 34, true));
  798. // And in the middle of one tag.
  799. $this->assertSame("<p>Žluťoučký koníček <b>přeskočil...</b></p>", shorten_text($text, 30, true));
  800. }
  801. public function test_shorten_text_utf8_oriental() {
  802. // Japanese
  803. // text without tags
  804. // ......123456789012345678901234.
  805. $text = '言語設定言語設定abcdefghijkl';
  806. $this->assertSame($text, shorten_text($text)); // 30 chars by default.
  807. $this->assertSame("言語設定言語...", shorten_text($text, 9, true));
  808. $this->assertSame("言語設定言語...", shorten_text($text, 9, false));
  809. $this->assertSame("言語設定言語設定ab...", shorten_text($text, 13, true));
  810. $this->assertSame("言語設定言語設定...", shorten_text($text, 13, false));
  811. // Chinese
  812. // text without tags
  813. // ......123456789012345678901234.
  814. $text = '简体中文简体中文abcdefghijkl';
  815. $this->assertSame($text, shorten_text($text)); // 30 chars by default.
  816. $this->assertSame("简体中文简体...", shorten_text($text, 9, true));
  817. $this->assertSame("简体中文简体...", shorten_text($text, 9, false));
  818. $this->assertSame("简体中文简体中文ab...", shorten_text($text, 13, true));
  819. $this->assertSame("简体中文简体中文...", shorten_text($text, 13, false));
  820. }
  821. public function test_shorten_text_multilang() {
  822. // This is not necessaryily specific to multilang. The issue is really
  823. // tags with attributes, where before we were generating invalid HTML
  824. // output like shorten_text('<span id="x" class="y">A</span> B', 1)
  825. // returning '<span id="x" ...</span>'. It is just that multilang
  826. // requires the sort of HTML that is quite likely to trigger this.
  827. // ........................................1...
  828. $text = '<span lang="en" class="multilang">A</span>' .
  829. '<span lang="fr" class="multilang">B</span>';
  830. $this->assertSame('<span lang="en" class="multilang">...</span>',
  831. shorten_text($text, 1));
  832. }
  833. public function test_usergetdate() {
  834. global $USER, $CFG, $DB;
  835. $this->resetAfterTest();
  836. // Check if forcetimezone is set then save it and set it to use user timezone.
  837. $cfgforcetimezone = null;
  838. if (isset($CFG->forcetimezone)) {
  839. $cfgforcetimezone = $CFG->forcetimezone;
  840. $CFG->forcetimezone = 99; // Get user default timezone.
  841. }
  842. $this->setAdminUser();
  843. $userstimezone = $USER->timezone;
  844. $USER->timezone = 2;// Set the timezone to a known state.
  845. // The string version of date comes from server locale setting and does
  846. // not respect user language, so it is necessary to reset that.
  847. $oldlocale = setlocale(LC_TIME, '0');
  848. setlocale(LC_TIME, 'en_AU.UTF-8');
  849. $ts = 1261540267; // The time this function was created.
  850. $arr = usergetdate($ts, 1); // Specify the timezone as an argument.
  851. $arr = array_values($arr);
  852. list($seconds, $minutes, $hours, $mday, $wday, $mon, $year, $yday, $weekday, $month) = $arr;
  853. $this->assertSame(7, $seconds);
  854. $this->assertSame(51, $minutes);
  855. $this->assertSame(4, $hours);
  856. $this->assertSame(23, $mday);
  857. $this->assertSame(3, $wday);
  858. $this->assertSame(12, $mon);
  859. $this->assertSame(2009, $year);
  860. $this->assertSame(356, $yday);
  861. $this->assertSame('Wednesday', $weekday);
  862. $this->assertSame('December', $month);
  863. $arr = usergetdate($ts); // Gets the timezone from the $USER object.
  864. $arr = array_values($arr);
  865. list($seconds, $minutes, $hours, $mday, $wday, $mon, $year, $yday, $weekday, $month) = $arr;
  866. $this->assertSame(7, $seconds);
  867. $this->assertSame(51, $minutes);
  868. $this->assertSame(5, $hours);
  869. $this->assertSame(23, $mday);
  870. $this->assertSame(3, $wday);
  871. $this->assertSame(12, $mon);
  872. $this->assertSame(2009, $year);
  873. $this->assertSame(356, $yday);
  874. $this->assertSame('Wednesday', $weekday);
  875. $this->assertSame('December', $month);
  876. // Set the timezone back to what it was.
  877. $USER->timezone = $userstimezone;
  878. // Restore forcetimezone if changed.
  879. if (!is_null($cfgforcetimezone)) {
  880. $CFG->forcetimezone = $cfgforcetimezone;
  881. }
  882. setlocale(LC_TIME, $oldlocale);
  883. }
  884. public function test_mark_user_preferences_changed() {
  885. $this->resetAfterTest();
  886. $otheruser = $this->getDataGenerator()->create_user();
  887. $otheruserid = $otheruser->id;
  888. set_cache_flag('userpreferenceschanged', $otheruserid, null);
  889. mark_user_preferences_changed($otheruserid);
  890. $this->assertEquals(get_cache_flag('userpreferenceschanged', $otheruserid, time()-10), 1);
  891. set_cache_flag('userpreferenceschanged', $otheruserid, null);
  892. }
  893. public function test_check_user_preferences_loaded() {
  894. global $DB;
  895. $this->resetAfterTest();
  896. $otheruser = $this->getDataGenerator()->create_user();
  897. $otheruserid = $otheruser->id;
  898. $DB->delete_records('user_preferences', array('userid'=>$otheruserid));
  899. set_cache_flag('userpreferenceschanged', $otheruserid, null);
  900. $user = new stdClass();
  901. $user->id = $otheruserid;
  902. // Load.
  903. check_user_preferences_loaded($user);
  904. $this->assertTrue(isset($user->preference));
  905. $this->assertTrue(is_array($user->preference));
  906. $this->assertArrayHasKey('_lastloaded', $user->preference);
  907. $this->assertCount(1, $user->preference);
  908. // Add preference via direct call.
  909. $DB->insert_record('user_preferences', array('name'=>'xxx', 'value'=>'yyy', 'userid'=>$user->id));
  910. // No cache reload yet.
  911. check_user_preferences_loaded($user);
  912. $this->assertCount(1, $user->preference);
  913. // Forced reloading of cache.
  914. unset($user->preference);
  915. check_user_preferences_loaded($user);
  916. $this->assertCount(2, $user->preference);
  917. $this->assertSame('yyy', $user->preference['xxx']);
  918. // Add preference via direct call.
  919. $DB->insert_record('user_preferences', array('name'=>'aaa', 'value'=>'bbb', 'userid'=>$user->id));
  920. // Test timeouts and modifications from different session.
  921. set_cache_flag('userpreferenceschanged', $user->id, 1, time() + 1000);
  922. $user->preference['_lastloaded'] = $user->preference['_lastloaded'] - 20;
  923. check_user_preferences_loaded($user);
  924. $this->assertCount(2, $user->preference);
  925. check_user_preferences_loaded($user, 10);
  926. $this->assertCount(3, $user->preference);
  927. $this->assertSame('bbb', $user->preference['aaa']);
  928. set_cache_flag('userpreferenceschanged', $user->id, null);
  929. }
  930. public function test_set_user_preference() {
  931. global $DB, $USER;
  932. $this->resetAfterTest();
  933. $this->setAdminUser();
  934. $otheruser = $this->getDataGenerator()->create_user();
  935. $otheruserid = $otheruser->id;
  936. $DB->delete_records('user_preferences', array('userid'=>$otheruserid));
  937. set_cache_flag('userpreferenceschanged', $otheruserid, null);
  938. $user = new stdClass();
  939. $user->id = $otheruserid;
  940. set_user_preference('aaa', 'bbb', $otheruserid);
  941. $this->assertSame('bbb', $DB->get_field('user_preferences', 'value', array('userid'=>$otheruserid, 'name'=>'aaa')));
  942. $this->assertSame('bbb', get_user_preferences('aaa', null, $otheruserid));
  943. set_user_preference('xxx', 'yyy', $user);
  944. $this->assertSame('yyy', $DB->get_field('user_preferences', 'value', array('userid'=>$otheruserid, 'name'=>'xxx')));
  945. $this->assertSame('yyy', get_user_preferences('xxx', null, $otheruserid));
  946. $this->assertTrue(is_array($user->preference));
  947. $this->assertSame('bbb', $user->preference['aaa']);
  948. $this->assertSame('yyy', $user->preference['xxx']);
  949. set_user_preference('xxx', null, $user);
  950. $this->assertFalse($DB->get_field('user_preferences', 'value', array('userid'=>$otheruserid, 'name'=>'xxx')));
  951. $this->assertNull(get_user_preferences('xxx', null, $otheruserid));
  952. set_user_preference('ooo', true, $user);
  953. $prefs = get_user_preferences(null, null, $otheruserid);
  954. $this->assertSame($user->preference['aaa'], $prefs['aaa']);
  955. $this->assertSame($user->preference['ooo'], $prefs['ooo']);
  956. $this->assertSame('1', $prefs['ooo']);
  957. set_user_preference('null', 0, $user);
  958. $this->assertSame('0', get_user_preferences('null', null, $otheruserid));
  959. $this->assertSame('lala', get_user_preferences('undefined', 'lala', $otheruserid));
  960. $DB->delete_records('user_preferences', array('userid'=>$otheruserid));
  961. set_cache_flag('userpreferenceschanged', $otheruserid, null);
  962. // Test $USER default.
  963. set_user_preference('_test_user_preferences_pref', 'ok');
  964. $this->assertSame('ok', $USER->preference['_test_user_preferences_pref']);
  965. unset_user_preference('_test_user_preferences_pref');
  966. $this->assertTrue(!isset($USER->preference['_test_user_preferences_pref']));
  967. // Test 1333 char values (no need for unicode, there are already tests for that in DB tests).
  968. $longvalue = str_repeat('a', 1333);
  969. set_user_preference('_test_long_user_preference', $longvalue);
  970. $this->assertEquals($longvalue, get_user_preferences('_test_long_user_preference'));
  971. $this->assertEquals($longvalue,
  972. $DB->get_field('user_preferences', 'value', array('userid' => $USER->id, 'name' => '_test_long_user_preference')));
  973. // Test > 1333 char values, coding_exception expected.
  974. $longvalue = str_repeat('a', 1334);
  975. try {
  976. set_user_preference('_test_long_user_preference', $longvalue);
  977. $this->fail('Exception expected - longer than 1333 chars not allowed as preference value');
  978. } catch (moodle_exception $ex) {
  979. $this->assertInstanceOf('coding_exception', $ex);
  980. }
  981. // Test invalid params.
  982. try {
  983. set_user_preference('_test_user_preferences_pref', array());
  984. $this->fail('Exception expected - array not valid preference value');
  985. } catch (moodle_exception $ex) {
  986. $this->assertInstanceOf('coding_exception', $ex);
  987. }
  988. try {
  989. set_user_preference('_test_user_preferences_pref', new stdClass);
  990. $this->fail('Exception expected - class not valid preference value');
  991. } catch (moodle_exception $ex) {
  992. $this->assertInstanceOf('coding_exception', $ex);
  993. }
  994. try {
  995. set_user_preference('_test_user_preferences_pref', 1, array('xx' => 1));
  996. $this->fail('Exception expected - user instance expected');
  997. } catch (moodle_exception $ex) {
  998. $this->assertInstanceOf('coding_exception', $ex);
  999. }
  1000. try {
  1001. set_user_preference('_test_user_preferences_pref', 1, 'abc');
  1002. $this->fail('Exception expected - user instance expected');
  1003. } catch (moodle_exception $ex) {
  1004. $this->assertInstanceOf('coding_exception', $ex);
  1005. }
  1006. try {
  1007. set_user_preference('', 1);
  1008. $this->fail('Exception expected - invalid name accepted');
  1009. } catch (moodle_exception $ex) {
  1010. $this->assertInstanceOf('coding_exception', $ex);
  1011. }
  1012. try {
  1013. set_user_preference('1', 1);
  1014. $this->fail('Exception expected - invalid name accepted');
  1015. } catch (moodle_exception $ex) {
  1016. $this->assertInstanceOf('coding_exception', $ex);
  1017. }
  1018. }
  1019. public function test_get_extra_user_fields() {
  1020. global $CFG, $USER, $DB;
  1021. $this->resetAfterTest();
  1022. $this->setAdminUser();
  1023. // It would be really nice if there were a way to 'mock' has_capability
  1024. // checks (either to return true or false) but as there is not, this
  1025. // test doesn't test the capability check. Presumably, anyone running
  1026. // unit tests will have the capability.
  1027. $context = context_system::instance();
  1028. // No fields.
  1029. $CFG->showuseridentity = '';
  1030. $this->assertEquals(array(), get_extra_user_fields($context));
  1031. // One field.
  1032. $CFG->showuseridentity = 'frog';
  1033. $this->assertEquals(array('frog'), get_extra_user_fields($context));
  1034. // Two fields.
  1035. $CFG->showuseridentity = 'frog,zombie';
  1036. $this->assertEquals(array('frog', 'zombie'), get_extra_user_fields($context));
  1037. // No fields, except.
  1038. $CFG->showuseridentity = '';
  1039. $this->assertEquals(array(), get_extra_user_fields($context, array('frog')));
  1040. // One field.
  1041. $CFG->showuseridentity = 'frog';
  1042. $this->assertEquals(array(), get_extra_user_fields($context, array('frog')));
  1043. // Two fields.
  1044. $CFG->showuseridentity = 'frog,zombie';
  1045. $this->assertEquals(array('zombie'), get_extra_user_fields($context, array('frog')));
  1046. }
  1047. public function test_get_extra_user_fields_sql() {
  1048. global $CFG, $USER, $DB;
  1049. $this->resetAfterTest();
  1050. $this->setAdminUser();
  1051. $context = context_system::instance();
  1052. // No fields.
  1053. $CFG->showuseridentity = '';
  1054. $this->assertSame('', get_extra_user_fields_sql($context));
  1055. // One field.
  1056. $CFG->showuseridentity = 'frog';
  1057. $this->assertSame(', frog', get_extra_user_fields_sql($context));
  1058. // Two fields with table prefix.
  1059. $CFG->showuseridentity = 'frog,zombie';
  1060. $this->assertSame(', u1.frog, u1.zombie', get_extra_user_fields_sql($context, 'u1'));
  1061. // Two fields with field prefix.
  1062. $CFG->showuseridentity = 'frog,zombie';
  1063. $this->assertSame(', frog AS u_frog, zombie AS u_zombie',
  1064. get_extra_user_fields_sql($context, '', 'u_'));
  1065. // One field excluded.
  1066. $CFG->showuseridentity = 'frog';
  1067. $this->assertSame('', get_extra_user_fields_sql($context, '', '', array('frog')));
  1068. // Two fields, one excluded, table+field prefix.
  1069. $CFG->showuseridentity = 'frog,zombie';
  1070. $this->assertEquals(', u1.zombie AS u_zombie',
  1071. get_extra_user_fields_sql($context, 'u1', 'u_', array('frog')));
  1072. }
  1073. /**
  1074. * Test some critical TZ/DST.
  1075. *
  1076. * This method tests some special TZ/DST combinations that were fixed
  1077. * by MDL-38999. The tests are done by comparing the results of the
  1078. * output using Moodle TZ/DST support and PHP native one.
  1079. *
  1080. * Note: If you don't trust PHP TZ/DST support, can verify the
  1081. * harcoded expectations below with:
  1082. * http://www.tools4noobs.com/online_tools/unix_timestamp_to_datetime/
  1083. */
  1084. public function test_some_moodle_special_dst() {
  1085. $stamp = 1365386400; // 2013/04/08 02:00:00 GMT/UTC.
  1086. // In Europe/Tallinn it was 2013/04/08 05:00:00.
  1087. $expectation = '2013/04/08 05:00:00';
  1088. $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC'));
  1089. $phpdt->setTimezone(new DateTimeZone('Europe/Tallinn'));
  1090. $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result.
  1091. $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'Europe/Tallinn', false); // Moodle result.
  1092. $this->assertSame($expectation, $phpres);
  1093. $this->assertSame($expectation, $moodleres);
  1094. // In St. Johns it was 2013/04/07 23:30:00.
  1095. $expectation = '2013/04/07 23:30:00';
  1096. $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC'));
  1097. $phpdt->setTimezone(new DateTimeZone('America/St_Johns'));
  1098. $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result.
  1099. $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'America/St_Johns', false); // Moodle result.
  1100. $this->assertSame($expectation, $phpres);
  1101. $this->assertSame($expectation, $moodleres);
  1102. $stamp = 1383876000; // 2013/11/08 02:00:00 GMT/UTC.
  1103. // In Europe/Tallinn it was 2013/11/08 04:00:00.
  1104. $expectation = '2013/11/08 04:00:00';
  1105. $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC'));
  1106. $phpdt->setTimezone(new DateTimeZone('Europe/Tallinn'));
  1107. $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result.
  1108. $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'Europe/Tallinn', false); // Moodle result.
  1109. $this->assertSame($expectation, $phpres);
  1110. $this->assertSame($expectation, $moodleres);
  1111. // In St. Johns it was 2013/11/07 22:30:00.
  1112. $expectation = '2013/11/07 22:30:00';
  1113. $phpdt = DateTime::createFromFormat('U', $stamp, new DateTimeZone('UTC'));
  1114. $phpdt->setTimezone(new DateTimeZone('America/St_Johns'));
  1115. $phpres = $phpdt->format('Y/m/d H:i:s'); // PHP result.
  1116. $moodleres = userdate($stamp, '%Y/%m/%d %H:%M:%S', 'America/St_Johns', false); // Moodle result.
  1117. $this->assertSame($expectation, $phpres);
  1118. $this->assertSame($expectation, $moodleres);
  1119. }
  1120. public function test_userdate() {
  1121. global $USER, $CFG, $DB;
  1122. $this->resetAfterTest();
  1123. $this->setAdminUser();
  1124. $testvalues = array(
  1125. array(
  1126. 'time' => '1309514400',
  1127. 'usertimezone' => 'America/Moncton',
  1128. 'timezone' => '0.0', // No dst offset.
  1129. 'expectedoutput' => 'Friday, 1 July 2011, 10:00 AM'
  1130. ),
  1131. array(
  1132. 'time' => '1309514400',
  1133. 'usertimezone' => 'America/Moncton',
  1134. 'timezone' => '99', // Dst offset and timezone offset.
  1135. 'expectedoutput' => 'Friday, 1 July 2011, 7:00 AM'
  1136. ),
  1137. array(
  1138. 'time' => '1309514400',
  1139. 'usertimezone' => 'America/Moncton',
  1140. 'timezone' => 'America/Moncton', // Dst offset and timezone offset.
  1141. 'expectedoutput' => 'Friday, 1 July 2011, 7:00 AM'
  1142. ),
  1143. array(
  1144. 'time' => '1293876000 ',
  1145. 'usertimezone' => 'America/Moncton',
  1146. 'timezone' => '0.0', // No dst offset.
  1147. 'expectedoutput' => 'Saturday, 1 January 2011, 10:00 AM'
  1148. ),
  1149. array(
  1150. 'time' => '1293876000 ',
  1151. 'usertimezone' => 'America/Moncton',
  1152. 'timezone' => '99', // No dst offset in jan, so just timezone offset.
  1153. 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 AM'
  1154. ),
  1155. array(
  1156. 'time' => '1293876000 ',
  1157. 'usertimezone' => 'America/Moncton',
  1158. 'timezone' => 'America/Moncton', // No dst offset in jan.
  1159. 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 AM'
  1160. ),
  1161. array(
  1162. 'time' => '1293876000 ',
  1163. 'usertimezone' => '2',
  1164. 'timezone' => '99', // Take user timezone.
  1165. 'expectedoutput' => 'Saturday, 1 January 2011, 12:00 PM'
  1166. ),
  1167. array(
  1168. 'time' => '1293876000 ',
  1169. 'usertimezone' => '-2',
  1170. 'timezone' => '99', // Take user timezone.
  1171. 'expectedoutput' => 'Saturday, 1 January 2011, 8:00 AM'
  1172. ),
  1173. array(
  1174. 'time' => '1293876000 ',
  1175. 'usertimezone' => '-10',
  1176. 'timezone' => '2', // Take this timezone.
  1177. 'expectedoutput' => 'Saturday, 1 January 2011, 12:00 PM'
  1178. ),
  1179. array(
  1180. 'time' => '1293876000 ',
  1181. 'usertimezone' => '-10',
  1182. 'timezone' => '-2', // Take this timezone.
  1183. 'expectedoutput' => 'Saturday, 1 January 2011, 8:00 AM'
  1184. ),
  1185. array(
  1186. 'time' => '1293876000 ',
  1187. 'usertimezone' => '-10',
  1188. 'timezone' => 'random/time', // This should show server time.
  1189. 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 PM'
  1190. ),
  1191. array(
  1192. 'time' => '1293876000 ',
  1193. 'usertimezone' => '14', // Server time zone.
  1194. 'timezone' => '99', // This should show user time.
  1195. 'expectedoutput' => 'Saturday, 1 January 2011, 6:00 PM'
  1196. ),
  1197. );
  1198. // Check if forcetimezone is set then save it and set it to use user timezone.
  1199. $cfgforcetimezone = null;
  1200. if (isset($CFG->forcetimezone)) {
  1201. $cfgforcetimezone = $CFG->forcetimezone;
  1202. $CFG->forcetimezone = 99; // Get user default timezone.
  1203. }
  1204. // Store user default timezone to restore later.
  1205. $userstimezone = $USER->timezone;
  1206. // The string version of date comes from server locale setting and does
  1207. // not respect user language, so it is necessary to reset that.
  1208. $oldlocale = setlocale(LC_TIME, '0');
  1209. setlocale(LC_TIME, 'en_AU.UTF-8');
  1210. // Set default timezone to Australia/Perth, else time calculated
  1211. // will not match expected values. Before that save system defaults.
  1212. $systemdefaulttimezone = date_default_timezone_get();
  1213. date_default_timezone_set('Australia/Perth');
  1214. foreach ($testvalues as $vals) {
  1215. $USER->timezone = $vals['usertimezone'];
  1216. $actualoutput = userdate($vals['time'], '%A, %d %B %Y, %I:%M %p', $vals['timezone']);
  1217. // On different systems case of AM PM changes so compare case insensitive.
  1218. $vals['expectedoutput'] = core_text::strtolower($vals['expectedoutput']);
  1219. $actualoutput = core_text::strtolower($actualoutput);
  1220. $this->assertSame($vals['expectedoutput'], $actualoutput,
  1221. "Expected: {$vals['expectedoutput']} => Actual: {$actualoutput},
  1222. Please check if timezones are updated (Site adminstration -> location -> update timezone)");
  1223. }
  1224. // Restore user timezone back to what it was.
  1225. $USER->timezone = $userstimezone;
  1226. // Restore forcetimezone.
  1227. if (!is_null($cfgforcetimezone)) {
  1228. $CFG->forcetimezone = $cfgforcetimezone;
  1229. }
  1230. // Restore system default values.
  1231. date_default_timezone_set($systemdefaulttimezone);
  1232. setlocale(LC_TIME, $oldlocale);
  1233. }
  1234. public function test_make_timestamp() {
  1235. global $USER, $CFG, $DB;
  1236. $this->resetAfterTest();
  1237. $this->setAdminUser();
  1238. $testvalues = array(
  1239. array(
  1240. 'usertimezone' => 'America/Moncton',
  1241. 'year' => '2011',
  1242. 'month' => '7',
  1243. 'day' => '1',
  1244. 'hour' => '10',
  1245. 'minutes' => '00',
  1246. 'seconds' => '00',
  1247. 'timezone' => '0.0',
  1248. 'applydst' => false, // No dst offset.
  1249. 'expectedoutput' => '1309514400' // 6pm at UTC+0.
  1250. ),
  1251. array(
  1252. 'usertimezone' => 'America/Moncton',
  1253. 'year' => '2011',
  1254. 'month' => '7',
  1255. 'day' => '1',
  1256. 'hour' => '10',
  1257. 'minutes' => '00',
  1258. 'seconds' => '00',
  1259. 'timezone' => '99', // User default timezone.
  1260. 'applydst' => false, // Don't apply dst.
  1261. 'expectedoutput' => '1309528800'
  1262. ),
  1263. array(
  1264. 'usertimezone' => 'America/Moncton',
  1265. 'year' => '2011',
  1266. 'month' => '7',
  1267. 'day' => '1',
  1268. 'hour' => '10',
  1269. 'minutes' => '00',
  1270. 'seconds' => '00',
  1271. 'timezone' => '99', // User default timezone.
  1272. 'applydst' => true, // Apply dst.
  1273. 'expectedoutput' => '1309525200'
  1274. ),
  1275. array(
  1276. 'usertimezone' => 'America/Moncton',
  1277. 'year' => '2011',
  1278. 'month' => '7',
  1279. 'day' => '1',
  1280. 'hour' => '10',
  1281. 'minutes' => '00',
  1282. 'seconds' => '00',
  1283. 'timezone' => 'America/Moncton', // String timezone.
  1284. 'applydst' => true, // Apply dst.
  1285. 'expectedoutput' => '1309525200'
  1286. ),
  1287. array(
  1288. 'usertimezone' => '2', // No dst applyed.
  1289. 'year' => '2011',
  1290. 'month' => '7',
  1291. 'day' => '1',
  1292. 'hour' => '10',
  1293. 'minutes' => '00',
  1294. 'seconds' => '00',
  1295. 'timezone' => '99', // Take user timezone.
  1296. 'applydst' => true, // Apply dst.
  1297. 'expectedoutput' => '1309507200'
  1298. ),
  1299. array(
  1300. 'usertimezone' => '-2', // No dst applyed.
  1301. 'year' => '2011',
  1302. 'month' => '7',
  1303. 'day' => '1',
  1304. 'hour' => '10',
  1305. 'minutes' => '00',
  1306. 'seconds' => '00',
  1307. 'timezone' => '99', // Take usertimezone.
  1308. 'applydst' => true, // Apply dst.
  1309. 'expectedoutput' => '1309521600'
  1310. ),
  1311. array(
  1312. 'usertimezone' => '-10', // No dst applyed.
  1313. 'year' => '2011',
  1314. 'month' => '7',
  1315. 'day' => '1',
  1316. 'hour' => '10',
  1317. 'minutes' => '00',
  1318. 'seconds' => '00',
  1319. 'timezone' => '2', // Take this timezone.
  1320. 'applydst' => true, // Apply dst.
  1321. 'expectedoutput' => '1309507200'
  1322. ),
  1323. array(
  1324. 'usertimezone' => '-10', // No dst applyed.
  1325. 'year' => '2011',
  1326. 'month' => '7',
  1327. 'day' => '1',
  1328. 'hour' => '10',
  1329. 'minutes' => '00',
  1330. 'seconds' => '00',
  1331. 'timezone' => '-2', // Take this timezone.
  1332. 'applydst' => true, // Apply dst.
  1333. 'expectedoutput' => '1309521600'
  1334. ),
  1335. array(
  1336. 'usertimezone' => '-10', // No dst applyed.
  1337. 'year' => '2011',
  1338. 'month' => '7',
  1339. 'day' => '1',
  1340. 'hour' => '10',
  1341. 'minutes' => '00',
  1342. 'seconds' => '00',
  1343. 'timezone' => 'random/time', // This should show server time.
  1344. 'applydst' => true, // Apply dst.
  1345. 'expectedoutput' => '1309485600'
  1346. ),
  1347. array(
  1348. 'usertimezone' => '14', // Server time.
  1349. 'year' => '2011',
  1350. 'month' => '7',
  1351. 'day' => '1',
  1352. 'hour' => '10',
  1353. 'minutes' => '00',
  1354. 'seconds' => '00',
  1355. 'timezone' => '99', // Get user time.
  1356. 'applydst' => true, // Apply dst.
  1357. 'expectedoutput' => '1309485600'
  1358. )
  1359. );
  1360. // Check if forcetimezone is set then save it and set it to use user timezone.
  1361. $cfgforcetimezone = null;
  1362. if (isset($CFG->forcetimezone)) {
  1363. $cfgforcetimezone = $CFG->forcetimezone;
  1364. $CFG->forcetimezone = 99; // Get user default timezone.
  1365. }
  1366. // Store user default timezone to restore later.
  1367. $userstimezone = $USER->timezone;
  1368. // The string version of date comes from server locale setting and does
  1369. // not respect user language, so it is necessary to reset that.
  1370. $oldlocale = setlocale(LC_TIME, '0');
  1371. setlocale(LC_TIME, 'en_AU.UTF-8');
  1372. // Set default timezone to Australia/Perth, else time calculated
  1373. // Will not match expected values. Before that save system defaults.
  1374. $systemdefaulttimezone = date_default_timezone_get();
  1375. date_default_timezone_set('Australia/Perth');
  1376. // Test make_timestamp with all testvals and assert if anything wrong.
  1377. foreach ($testvalues as $vals) {
  1378. $USER->timezone = $vals['usertimezone'];
  1379. $actualoutput = make_timestamp(
  1380. $vals['year'],
  1381. $vals['month'],
  1382. $vals['day'],
  1383. $vals['hour'],
  1384. $vals['minutes'],
  1385. $vals['seconds'],
  1386. $vals['timezone'],
  1387. $vals['applydst']
  1388. );
  1389. // On different systems case of AM PM changes so compare case insensitive.
  1390. $vals['expectedoutput'] = core_text::strtolower($vals['expectedoutput']);
  1391. $actualoutput = core_text::strtolower($actualoutput);
  1392. $this->assertSame($vals['expectedoutput'], $actualoutput,
  1393. "Expected: {$vals['expectedoutput']} => Actual: {$actualoutput},
  1394. Please check if timezones are updated (Site adminstration -> location -> update timezone)");
  1395. }
  1396. // Restore user timezone back to what it was.
  1397. $USER->timezone = $userstimezone;
  1398. // Restore forcetimezone.
  1399. if (!is_null($cfgforcetimezone)) {
  1400. $CFG->forcetimezone = $cfgforcetimezone;
  1401. }
  1402. // Restore system default values.
  1403. date_default_timezone_set($systemdefaulttimezone);
  1404. setlocale(LC_TIME, $oldlocale);
  1405. }
  1406. /**
  1407. * Test get_string and most importantly the implementation of the lang_string
  1408. * object.
  1409. */
  1410. public function test_get_string() {
  1411. global $COURSE;
  1412. // Make sure we are using English.
  1413. $originallang = $COURSE->lang;
  1414. $COURSE->lang = 'en';
  1415. $yes = get_string('yes');
  1416. $yesexpected = 'Yes';
  1417. $this->assertInternalType('string', $yes);
  1418. $this->assertSame($yesexpected, $yes);
  1419. $yes = get_string('yes', 'moodle');
  1420. $this->assertInternalType('string', $yes);
  1421. $this->assertSame($yesexpected, $yes);
  1422. $yes = get_string('yes', 'core');
  1423. $this->assertInternalType('string', $yes);
  1424. $this->assertSame($yesexpected, $yes);
  1425. $yes = get_string('yes', '');
  1426. $this->assertInternalType('string', $yes);
  1427. $this->assertSame($yesexpected, $yes);
  1428. $yes = get_string('yes', null);
  1429. $this->assertInternalType('string', $yes);
  1430. $this->assertSame($yesexpected, $yes);
  1431. $yes = get_string('yes', null, 1);
  1432. $this->assertInternalType('string', $yes);
  1433. $this->assertSame($yesexpected, $yes);
  1434. $days = 1;
  1435. $numdays = get_string('numdays', 'core', '1');
  1436. $numdaysexpected = $days.' days';
  1437. $this->assertInternalType('string', $numdays);
  1438. $this->assertSame($numdaysexpected, $numdays);
  1439. $yes = get_string('yes', null, null, true);
  1440. $this->assertInstanceOf('lang_string', $yes);
  1441. $this->assertSame($yesexpected, (string)$yes);
  1442. // Test using a lang_string object as the $a argument for a normal
  1443. // get_string call (returning string).
  1444. $test = new lang_string('yes', null, null, true);
  1445. $testexpected = get_string('numdays', 'core', get_string('yes'));
  1446. $testresult = get_string('numdays', null, $test);
  1447. $this->assertInternalType('string', $testresult);
  1448. $this->assertSame($testexpected, $testresult);
  1449. // Test using a lang_string object as the $a argument for an object
  1450. // get_string call (returning lang_string).
  1451. $test = new lang_string('yes', null, null, true);
  1452. $testexpected = get_string('numdays', 'core', get_string('yes'));
  1453. $testresult = get_string('numdays', null, $test, true);
  1454. $this->assertInstanceOf('lang_string', $testresult);
  1455. $this->assertSame($testexpected, "$testresult");
  1456. // Make sure that object properties that can't be converted don't cause
  1457. // errors.
  1458. // Level one: This is as deep as current language processing goes.
  1459. $test = new stdClass;
  1460. $test->one = 'here';
  1461. $string = get_string('yes', null, $test, true);
  1462. $this->assertEquals($yesexpected, $string);
  1463. // Make sure that object properties that can't be converted don't cause
  1464. // errors.
  1465. // Level two: Language processing doesn't currently reach this deep.
  1466. // only immediate scalar properties are worked with.
  1467. $test = new stdClass;
  1468. $test->one = new stdClass;
  1469. $test->one->two = 'here';
  1470. $string = get_string('yes', null, $test, true);
  1471. $this->assertEquals($yesexpected, $string);
  1472. // Make sure that object properties that can't be converted don't cause
  1473. // errors.
  1474. // Level three: It should never ever go this deep, but we're making sure
  1475. // it doesn't cause any probs anyway.
  1476. $test = new stdClass;
  1477. $test->one = new stdClass;
  1478. $test->one->two = new stdClass;
  1479. $test->one->two->three = 'here';
  1480. $string = get_string('yes', null, $test, true);
  1481. $this->assertEquals($yesexpected, $string);
  1482. // Make sure that object properties that can't be converted don't cause
  1483. // errors and check lang_string properties.
  1484. // Level one: This is as deep as current language processing goes.
  1485. $test = new stdClass;
  1486. $test->one = new lang_string('yes');
  1487. $string = get_string('yes', null, $test, true);
  1488. $this->assertEquals($yesexpected, $string);
  1489. // Make sure that object properties that can't be converted don't cause
  1490. // errors and check lang_string properties.
  1491. // Level two: Language processing doesn't currently reach this deep.
  1492. // only immediate scalar properties are worked with.
  1493. $test = new stdClass;
  1494. $test->one = new stdClass;
  1495. $test->one->two = new lang_string('yes');
  1496. $string = get_string('yes', null, $test, true);
  1497. $this->assertEquals($yesexpected, $string);
  1498. // Make sure that object properties that can't be converted don't cause
  1499. // errors and check lang_string properties.
  1500. // Level three: It should never ever go this deep, but we're making sure
  1501. // it doesn't cause any probs anyway.
  1502. $test = new stdClass;
  1503. $test->one = new stdClass;
  1504. $test->one->two = new stdClass;
  1505. $test->one->two->three = new lang_string('yes');
  1506. $string = get_string('yes', null, $test, true);
  1507. $this->assertEquals($yesexpected, $string);
  1508. // Make sure that array properties that can't be converted don't cause
  1509. // errors.
  1510. $test = array();
  1511. $test['one'] = new stdClass;
  1512. $test['one']->two = 'here';
  1513. $string = get_string('yes', null, $test, true);
  1514. $this->assertEquals($yesexpected, $string);
  1515. // Same thing but as above except using an object... this is allowed :P.
  1516. $string = get_string('yes', null, null, true);
  1517. $object = new stdClass;
  1518. $object->$string = 'Yes';
  1519. $this->assertEquals($yesexpected, $string);
  1520. $this->assertEquals($yesexpected, $object->$string);
  1521. // Reset the language.
  1522. $COURSE->lang = $originallang;
  1523. }
  1524. /**
  1525. * @expectedException PHPUnit_Framework_Error_Warning
  1526. */
  1527. public function test_get_string_limitation() {
  1528. // This is one of the limitations to the lang_string class. It can't be
  1529. // used as a key.
  1530. $array = array(get_string('yes', null, null, true) => 'yes');
  1531. }
  1532. /**
  1533. * Test localised float formatting.
  1534. */
  1535. public function test_format_float() {
  1536. // Special case for null.
  1537. $this->assertEquals('', format_float(null));
  1538. // Default 1 decimal place.
  1539. $this->assertEquals('5.4', format_float(5.43));
  1540. $this->assertEquals('5.0', format_float(5.001));
  1541. // Custom number of decimal places.
  1542. $this->assertEquals('5.43000', format_float(5.43, 5));
  1543. // Option to strip ending zeros after rounding.
  1544. $this->assertEquals('5.43', format_float(5.43, 5, true, true));
  1545. $this->assertEquals('5', format_float(5.0001, 3, true, true));
  1546. // Tests with a localised decimal separator.
  1547. $this->define_local_decimal_separator();
  1548. // Localisation on (default).
  1549. $this->assertEquals('5X43000', format_float(5.43, 5));
  1550. $this->assertEquals('5X43', format_float(5.43, 5, true, true));
  1551. // Localisation off.
  1552. $this->assertEquals('5.43000', format_float(5.43, 5, false));
  1553. $this->assertEquals('5.43', format_float(5.43, 5, false, true));
  1554. }
  1555. /**
  1556. * Test localised float unformatting.
  1557. */
  1558. public function test_unformat_float() {
  1559. // Tests without the localised decimal separator.
  1560. // Special case for null, empty or white spaces only strings.
  1561. $this->assertEquals(null, unformat_float(null));
  1562. $this->assertEquals(null, unformat_float(''));
  1563. $this->assertEquals(null, unformat_float(' '));
  1564. // Regular use.
  1565. $this->assertEquals(5.4, unformat_float('5.4'));
  1566. $this->assertEquals(5.4, unformat_float('5.4', true));
  1567. // No decimal.
  1568. $this->assertEquals(5.0, unformat_float('5'));
  1569. // Custom number of decimal.
  1570. $this->assertEquals(5.43267, unformat_float('5.43267'));
  1571. // Empty decimal.
  1572. $this->assertEquals(100.0, unformat_float('100.00'));
  1573. // With the thousand separator.
  1574. $this->assertEquals(1000.0, unformat_float('1 000'));
  1575. $this->assertEquals(1000.32, unformat_float('1 000.32'));
  1576. // Negative number.
  1577. $this->assertEquals(-100.0, unformat_float('-100'));
  1578. // Wrong value.
  1579. $this->assertEquals(0.0, unformat_float('Wrong value'));
  1580. // Wrong value in strict mode.
  1581. $this->assertFalse(unformat_float('Wrong value', true));
  1582. // Combining options.
  1583. $this->assertEquals(-1023.862567, unformat_float(' -1 023.862567 '));
  1584. // Bad decimal separator (should crop the decimal).
  1585. $this->assertEquals(50.0, unformat_float('50,57'));
  1586. // Bad decimal separator in strict mode (should return false).
  1587. $this->assertFalse(unformat_float('50,57', true));
  1588. // Tests with a localised decimal separator.
  1589. $this->define_local_decimal_separator();
  1590. // We repeat the tests above but with the current decimal separator.
  1591. // Regular use without and with the localised separator.
  1592. $this->assertEquals (5.4, unformat_float('5.4'));
  1593. $this->assertEquals (5.4, unformat_float('5X4'));
  1594. // Custom number of decimal.
  1595. $this->assertEquals (5.43267, unformat_float('5X43267'));
  1596. // Empty decimal.
  1597. $this->assertEquals (100.0, unformat_float('100X00'));
  1598. // With the thousand separator.
  1599. $this->assertEquals (1000.32, unformat_float('1 000X32'));
  1600. // Bad different separator (should crop the decimal).
  1601. $this->assertEquals (50.0, unformat_float('50Y57'));
  1602. // Bad different separator in strict mode (should return false).
  1603. $this->assertFalse (unformat_float('50Y57', true));
  1604. // Combining options.
  1605. $this->assertEquals (-1023.862567, unformat_float(' -1 023X862567 '));
  1606. // Combining options in strict mode.
  1607. $this->assertEquals (-1023.862567, unformat_float(' -1 023X862567 ', true));
  1608. }
  1609. /**
  1610. * Test deleting of users.
  1611. */
  1612. public function test_delete_user() {
  1613. global $DB, $CFG;
  1614. $this->resetAfterTest();
  1615. $guest = $DB->get_record('user', array('id'=>$CFG->siteguest), '*', MUST_EXIST);
  1616. $admin = $DB->get_record('user', array('id'=>$CFG->siteadmins), '*', MUST_EXIST);
  1617. $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
  1618. $user = $this->getDataGenerator()->create_user(array('idnumber'=>'abc'));
  1619. $user2 = $this->getDataGenerator()->create_user(array('idnumber'=>'xyz'));
  1620. // Delete user and capture event.
  1621. $sink = $this->redirectEvents();
  1622. $result = delete_user($user);
  1623. $events = $sink->get_events();
  1624. $sink->close();
  1625. $event = array_pop($events);
  1626. // Test user is deleted in DB.
  1627. $this->assertTrue($result);
  1628. $deluser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST);
  1629. $this->assertEquals(1, $deluser->deleted);
  1630. $this->assertEquals(0, $deluser->picture);
  1631. $this->assertSame('', $deluser->idnumber);
  1632. $this->assertSame(md5($user->username), $deluser->email);
  1633. $this->assertRegExp('/^'.preg_quote($user->email, '/').'\.\d*$/', $deluser->username);
  1634. $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
  1635. // Test Event.
  1636. $this->assertInstanceOf('\core\event\user_deleted', $event);
  1637. $this->assertSame($user->id, $event->objectid);
  1638. $this->assertSame('user_deleted', $event->get_legacy_eventname());
  1639. $this->assertEventLegacyData($user, $event);
  1640. $expectedlogdata = array(SITEID, 'user', 'delete', "view.php?id=$user->id", $user->firstname.' '.$user->lastname);
  1641. $this->assertEventLegacyLogData($expectedlogdata, $event);
  1642. $eventdata = $event->get_data();
  1643. $this->assertSame($eventdata['other']['username'], $user->username);
  1644. $this->assertSame($eventdata['other']['email'], $user->email);
  1645. $this->assertSame($eventdata['other']['idnumber'], $user->idnumber);
  1646. $this->assertSame($eventdata['other']['picture'], $user->picture);
  1647. $this->assertSame($eventdata['other']['mnethostid'], $user->mnethostid);
  1648. $this->assertEquals($user, $event->get_record_snapshot('user', $event->objectid));
  1649. // Try invalid params.
  1650. $record = new stdClass();
  1651. $record->grrr = 1;
  1652. try {
  1653. delete_user($record);
  1654. $this->fail('Expecting exception for invalid delete_user() $user parameter');
  1655. } catch (moodle_exception $ex) {
  1656. $this->assertInstanceOf('coding_exception', $ex);
  1657. }
  1658. $record->id = 1;
  1659. try {
  1660. delete_user($record);
  1661. $this->fail('Expecting exception for invalid delete_user() $user parameter');
  1662. } catch (moodle_exception $ex) {
  1663. $this->assertInstanceOf('coding_exception', $ex);
  1664. }
  1665. $record = new stdClass();
  1666. $record->id = 666;
  1667. $record->username = 'xx';
  1668. $this->assertFalse($DB->record_exists('user', array('id'=>666))); // Any non-existent id is ok.
  1669. $result = delete_user($record);
  1670. $this->assertFalse($result);
  1671. $result = delete_user($guest);
  1672. $this->assertFalse($result);
  1673. $result = delete_user($admin);
  1674. $this->assertFalse($result);
  1675. $this->resetDebugging();
  1676. }
  1677. /**
  1678. * Test function convert_to_array()
  1679. */
  1680. public function test_convert_to_array() {
  1681. // Check that normal classes are converted to arrays the same way as (array) would do.
  1682. $obj = new stdClass();
  1683. $obj->prop1 = 'hello';
  1684. $obj->prop2 = array('first', 'second', 13);
  1685. $obj->prop3 = 15;
  1686. $this->assertEquals(convert_to_array($obj), (array)$obj);
  1687. // Check that context object (with iterator) is converted to array properly.
  1688. $obj = context_system::instance();
  1689. $ar = array(
  1690. 'id' => $obj->id,
  1691. 'contextlevel' => $obj->contextlevel,
  1692. 'instanceid' => $obj->instanceid,
  1693. 'path' => $obj->path,
  1694. 'depth' => $obj->depth
  1695. );
  1696. $this->assertEquals(convert_to_array($obj), $ar);
  1697. }
  1698. /**
  1699. * Test the function date_format_string().
  1700. */
  1701. public function test_date_format_string() {
  1702. global $CFG;
  1703. // Forcing locale and timezone.
  1704. $oldlocale = setlocale(LC_TIME, '0');
  1705. if ($CFG->ostype == 'WINDOWS') {
  1706. setlocale(LC_TIME, 'English_Australia.1252');
  1707. } else {
  1708. setlocale(LC_TIME, 'en_AU.UTF-8');
  1709. }
  1710. $systemdefaulttimezone = date_default_timezone_get();
  1711. date_default_timezone_set('Australia/Perth');
  1712. $tests = array(
  1713. array(
  1714. 'tz' => 99,
  1715. 'str' => '%A, %d %B %Y, %I:%M %p',
  1716. 'expected' => 'Saturday, 01 January 2011, 06:00 PM'
  1717. ),
  1718. array(
  1719. 'tz' => 0,
  1720. 'str' => '%A, %d %B %Y, %I:%M %p',
  1721. 'expected' => 'Saturday, 01 January 2011, 10:00 AM'
  1722. ),
  1723. array(
  1724. 'tz' => -12,
  1725. 'str' => '%A, %d %B %Y, %I:%M %p',
  1726. 'expected' => 'Saturday, 01 January 2011, 10:00 AM'
  1727. ),
  1728. // Following tests pass on Windows only because en lang pack does
  1729. // not contain localewincharset, in real life lang pack maintainers
  1730. // may use only characters that are present in localewincharset
  1731. // in format strings!
  1732. array(
  1733. 'tz' => 99,
  1734. 'str' => 'Žluťoučký koníček %A',
  1735. 'expected' => 'Žluťoučký koníček Saturday'
  1736. ),
  1737. array(
  1738. 'tz' => 99,
  1739. 'str' => '言語設定言語 %A',
  1740. 'expected' => '言語設定言語 Saturday'
  1741. ),
  1742. array(
  1743. 'tz' => 99,
  1744. 'str' => '简体中文简体 %A',
  1745. 'expected' => '简体中文简体 Saturday'
  1746. ),
  1747. );
  1748. // Note: date_format_string() uses the timezone only to differenciate
  1749. // the server time from the UTC time. It does not modify the timestamp.
  1750. // Hence similar results for timezones <= 13.
  1751. // On different systems case of AM PM changes so compare case insensitive.
  1752. foreach ($tests as $test) {
  1753. $str = date_format_string(1293876000, $test['str'], $test['tz']);
  1754. $this->assertSame(core_text::strtolower($test['expected']), core_text::strtolower($str));
  1755. }
  1756. // Restore system default values.
  1757. date_default_timezone_set($systemdefaulttimezone);
  1758. setlocale(LC_TIME, $oldlocale);
  1759. }
  1760. public function test_get_config() {
  1761. global $CFG;
  1762. $this->resetAfterTest();
  1763. // Preparation.
  1764. set_config('phpunit_test_get_config_1', 'test 1');
  1765. set_config('phpunit_test_get_config_2', 'test 2', 'mod_forum');
  1766. if (!is_array($CFG->config_php_settings)) {
  1767. $CFG->config_php_settings = array();
  1768. }
  1769. $CFG->config_php_settings['phpunit_test_get_config_3'] = 'test 3';
  1770. if (!is_array($CFG->forced_plugin_settings)) {
  1771. $CFG->forced_plugin_settings = array();
  1772. }
  1773. if (!array_key_exists('mod_forum', $CFG->forced_plugin_settings)) {
  1774. $CFG->forced_plugin_settings['mod_forum'] = array();
  1775. }
  1776. $CFG->forced_plugin_settings['mod_forum']['phpunit_test_get_config_4'] = 'test 4';
  1777. $CFG->phpunit_test_get_config_5 = 'test 5';
  1778. // Testing.
  1779. $this->assertSame('test 1', get_config('core', 'phpunit_test_get_config_1'));
  1780. $this->assertSame('test 2', get_config('mod_forum', 'phpunit_test_get_config_2'));
  1781. $this->assertSame('test 3', get_config('core', 'phpunit_test_get_config_3'));
  1782. $this->assertSame('test 4', get_config('mod_forum', 'phpunit_test_get_config_4'));
  1783. $this->assertFalse(get_config('core', 'phpunit_test_get_config_5'));
  1784. $this->assertFalse(get_config('core', 'phpunit_test_get_config_x'));
  1785. $this->assertFalse(get_config('mod_forum', 'phpunit_test_get_config_x'));
  1786. // Test config we know to exist.
  1787. $this->assertSame($CFG->dataroot, get_config('core', 'dataroot'));
  1788. $this->assertSame($CFG->phpunit_dataroot, get_config('core', 'phpunit_dataroot'));
  1789. $this->assertSame($CFG->dataroot, get_config('core', 'phpunit_dataroot'));
  1790. $this->assertSame(get_config('core', 'dataroot'), get_config('core', 'phpunit_dataroot'));
  1791. // Test setting a config var that already exists.
  1792. set_config('phpunit_test_get_config_1', 'test a');
  1793. $this->assertSame('test a', $CFG->phpunit_test_get_config_1);
  1794. $this->assertSame('test a', get_config('core', 'phpunit_test_get_config_1'));
  1795. // Test cache invalidation.
  1796. $cache = cache::make('core', 'config');
  1797. $this->assertInternalType('array', $cache->get('core'));
  1798. $this->assertInternalType('array', $cache->get('mod_forum'));
  1799. set_config('phpunit_test_get_config_1', 'test b');
  1800. $this->assertFalse($cache->get('core'));
  1801. set_config('phpunit_test_get_config_4', 'test c', 'mod_forum');
  1802. $this->assertFalse($cache->get('mod_forum'));
  1803. }
  1804. public function test_get_max_upload_sizes() {
  1805. // Test with very low limits so we are not affected by php upload limits.
  1806. // Test activity limit smallest.
  1807. $sitebytes = 102400;
  1808. $coursebytes = 51200;
  1809. $modulebytes = 10240;
  1810. $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
  1811. $this->assertSame('Activity upload limit (10KB)', $result['0']);
  1812. $this->assertCount(2, $result);
  1813. // Test course limit smallest.
  1814. $sitebytes = 102400;
  1815. $coursebytes = 10240;
  1816. $modulebytes = 51200;
  1817. $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
  1818. $this->assertSame('Course upload limit (10KB)', $result['0']);
  1819. $this->assertCount(2, $result);
  1820. // Test site limit smallest.
  1821. $sitebytes = 10240;
  1822. $coursebytes = 102400;
  1823. $modulebytes = 51200;
  1824. $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
  1825. $this->assertSame('Site upload limit (10KB)', $result['0']);
  1826. $this->assertCount(2, $result);
  1827. // Test site limit not set.
  1828. $sitebytes = 0;
  1829. $coursebytes = 102400;
  1830. $modulebytes = 51200;
  1831. $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
  1832. $this->assertSame('Activity upload limit (50KB)', $result['0']);
  1833. $this->assertCount(3, $result);
  1834. $sitebytes = 0;
  1835. $coursebytes = 51200;
  1836. $modulebytes = 102400;
  1837. $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes);
  1838. $this->assertSame('Course upload limit (50KB)', $result['0']);
  1839. $this->assertCount(3, $result);
  1840. // Test custom bytes in range.
  1841. $sitebytes = 102400;
  1842. $coursebytes = 51200;
  1843. $modulebytes = 51200;
  1844. $custombytes = 10240;
  1845. $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes);
  1846. $this->assertCount(3, $result);
  1847. // Test custom bytes in range but non-standard.
  1848. $sitebytes = 102400;
  1849. $coursebytes = 51200;
  1850. $modulebytes = 51200;
  1851. $custombytes = 25600;
  1852. $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes);
  1853. $this->assertCount(4, $result);
  1854. // Test custom bytes out of range.
  1855. $sitebytes = 102400;
  1856. $coursebytes = 51200;
  1857. $modulebytes = 51200;
  1858. $custombytes = 102400;
  1859. $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes);
  1860. $this->assertCount(3, $result);
  1861. // Test custom bytes out of range and non-standard.
  1862. $sitebytes = 102400;
  1863. $coursebytes = 51200;
  1864. $modulebytes = 51200;
  1865. $custombytes = 256000;
  1866. $result = get_max_upload_sizes($sitebytes, $coursebytes, $modulebytes, $custombytes);
  1867. $this->assertCount(3, $result);
  1868. // Test site limit only.
  1869. $sitebytes = 51200;
  1870. $result = get_max_upload_sizes($sitebytes);
  1871. $this->assertSame('Site upload limit (50KB)', $result['0']);
  1872. $this->assertSame('50KB', $result['51200']);
  1873. $this->assertSame('10KB', $result['10240']);
  1874. $this->assertCount(3, $result);
  1875. // Test no limit.
  1876. $result = get_max_upload_sizes();
  1877. $this->assertArrayHasKey('0', $result);
  1878. $this->assertArrayHasKey(get_max_upload_file_size(), $result);
  1879. }
  1880. /**
  1881. * Test function password_is_legacy_hash().
  1882. */
  1883. public function test_password_is_legacy_hash() {
  1884. // Well formed md5s should be matched.
  1885. foreach (array('some', 'strings', 'to_check!') as $string) {
  1886. $md5 = md5($string);
  1887. $this->assertTrue(password_is_legacy_hash($md5));
  1888. }
  1889. // Strings that are not md5s should not be matched.
  1890. foreach (array('', AUTH_PASSWORD_NOT_CACHED, 'IPW8WTcsWNgAWcUS1FBVHegzJnw5M2jOmYkmfc8z.xdBOyC4Caeum') as $notmd5) {
  1891. $this->assertFalse(password_is_legacy_hash($notmd5));
  1892. }
  1893. }
  1894. /**
  1895. * Test function validate_internal_user_password().
  1896. */
  1897. public function test_validate_internal_user_password() {
  1898. if (password_compat_not_supported()) {
  1899. // If bcrypt is not properly supported test legacy md5 hashes instead.
  1900. // Can't hardcode these as we don't know the site's password salt.
  1901. $validhashes = array(
  1902. 'pw' => hash_internal_user_password('pw'),
  1903. 'abc' => hash_internal_user_password('abc'),
  1904. 'C0mP1eX_&}<?@*&%` |\"' => hash_internal_user_password('C0mP1eX_&}<?@*&%` |\"'),
  1905. 'ĩńťėŕňăţĩōŋāĹ' => hash_internal_user_password('ĩńťėŕňăţĩōŋāĹ')
  1906. );
  1907. } else {
  1908. // Otherwise test bcrypt hashes.
  1909. $validhashes = array(
  1910. 'pw' => '$2y$10$LOSDi5eaQJhutSRun.OVJ.ZSxQZabCMay7TO1KmzMkDMPvU40zGXK',
  1911. 'abc' => '$2y$10$VWTOhVdsBbWwtdWNDRHSpewjd3aXBQlBQf5rBY/hVhw8hciarFhXa',
  1912. 'C0mP1eX_&}<?@*&%` |\"' => '$2y$10$3PJf.q.9ywNJlsInPbqc8.IFeSsvXrGvQLKRFBIhVu1h1I3vpIry6',
  1913. 'ĩńťėŕňăţĩōŋāĹ' => '$2y$10$3A2Y8WpfRAnP3czJiSv6N.6Xp0T8hW3QZz2hUCYhzyWr1kGP1yUve'
  1914. );
  1915. }
  1916. foreach ($validhashes as $password => $hash) {
  1917. $user = new stdClass();
  1918. $user->auth = 'manual';
  1919. $user->password = $hash;
  1920. // The correct password should be validated.
  1921. $this->assertTrue(validate_internal_user_password($user, $password));
  1922. // An incorrect password should not be validated.
  1923. $this->assertFalse(validate_internal_user_password($user, 'badpw'));
  1924. }
  1925. }
  1926. /**
  1927. * Test function hash_internal_user_password().
  1928. */
  1929. public function test_hash_internal_user_password() {
  1930. $passwords = array('pw', 'abc123', 'C0mP1eX_&}<?@*&%` |\"', 'ĩńťėŕňăţĩōŋāĹ');
  1931. // Check that some passwords that we convert to hashes can
  1932. // be validated.
  1933. foreach ($passwords as $password) {
  1934. $hash = hash_internal_user_password($password);
  1935. $fasthash = hash_internal_user_password($password, true);
  1936. $user = new stdClass();
  1937. $user->auth = 'manual';
  1938. $user->password = $hash;
  1939. $this->assertTrue(validate_internal_user_password($user, $password));
  1940. if (password_compat_not_supported()) {
  1941. // If bcrypt is not properly supported make sure the passwords are in md5 format.
  1942. $this->assertTrue(password_is_legacy_hash($hash));
  1943. } else {
  1944. // Otherwise they should not be in md5 format.
  1945. $this->assertFalse(password_is_legacy_hash($hash));
  1946. // Check that cost factor in hash is correctly set.
  1947. $this->assertRegExp('/\$10\$/', $hash);
  1948. $this->assertRegExp('/\$04\$/', $fasthash);
  1949. }
  1950. }
  1951. }
  1952. /**
  1953. * Test function update_internal_user_password().
  1954. */
  1955. public function test_update_internal_user_password() {
  1956. global $DB;
  1957. $this->resetAfterTest();
  1958. $passwords = array('password', '1234', 'changeme', '****');
  1959. foreach ($passwords as $password) {
  1960. $user = $this->getDataGenerator()->create_user(array('auth'=>'manual'));
  1961. update_internal_user_password($user, $password);
  1962. // The user object should have been updated.
  1963. $this->assertTrue(validate_internal_user_password($user, $password));
  1964. // The database field for the user should also have been updated to the
  1965. // same value.
  1966. $this->assertSame($user->password, $DB->get_field('user', 'password', array('id' => $user->id)));
  1967. }
  1968. $user = $this->getDataGenerator()->create_user(array('auth'=>'manual'));
  1969. // Manually set the user's password to the md5 of the string 'password'.
  1970. $DB->set_field('user', 'password', '5f4dcc3b5aa765d61d8327deb882cf99', array('id' => $user->id));
  1971. // Update the password.
  1972. update_internal_user_password($user, 'password');
  1973. if (password_compat_not_supported()) {
  1974. // If bcrypt not properly supported the password should remain as an md5 hash.
  1975. $expected_hash = hash_internal_user_password('password', true);
  1976. $this->assertSame($user->password, $expected_hash);
  1977. $this->assertTrue(password_is_legacy_hash($user->password));
  1978. } else {
  1979. // Otherwise password should have been updated to a bcrypt hash.
  1980. $this->assertFalse(password_is_legacy_hash($user->password));
  1981. }
  1982. }
  1983. public function test_fullname() {
  1984. global $CFG;
  1985. $this->resetAfterTest();
  1986. // Create a user to test the name display on.
  1987. $record = array();
  1988. $record['firstname'] = 'Scott';
  1989. $record['lastname'] = 'Fletcher';
  1990. $record['firstnamephonetic'] = 'スコット';
  1991. $record['lastnamephonetic'] = 'フレチャー';
  1992. $record['alternatename'] = 'No friends';
  1993. $user = $this->getDataGenerator()->create_user($record);
  1994. // Back up config settings for restore later.
  1995. $originalcfg = new stdClass();
  1996. $originalcfg->fullnamedisplay = $CFG->fullnamedisplay;
  1997. // Testing existing fullnamedisplay settings.
  1998. $CFG->fullnamedisplay = 'firstname';
  1999. $testname = fullname($user);
  2000. $this->assertSame($user->firstname, $testname);
  2001. $CFG->fullnamedisplay = 'firstname lastname';
  2002. $expectedname = "$user->firstname $user->lastname";
  2003. $testname = fullname($user);
  2004. $this->assertSame($expectedname, $testname);
  2005. $CFG->fullnamedisplay = 'lastname firstname';
  2006. $expectedname = "$user->lastname $user->firstname";
  2007. $testname = fullname($user);
  2008. $this->assertSame($expectedname, $testname);
  2009. $expectedname = get_string('fullnamedisplay', null, $user);
  2010. $CFG->fullnamedisplay = 'language';
  2011. $testname = fullname($user);
  2012. $this->assertSame($expectedname, $testname);
  2013. // Test override parameter.
  2014. $CFG->fullnamedisplay = 'firstname';
  2015. $expectedname = "$user->firstname $user->lastname";
  2016. $testname = fullname($user, true);
  2017. $this->assertSame($expectedname, $testname);
  2018. // Test additional name fields.
  2019. $CFG->fullnamedisplay = 'lastname lastnamephonetic firstname firstnamephonetic';
  2020. $expectedname = "$user->lastname $user->lastnamephonetic $user->firstname $user->firstnamephonetic";
  2021. $testname = fullname($user);
  2022. $this->assertSame($expectedname, $testname);
  2023. // Test for handling missing data.
  2024. $user->middlename = null;
  2025. // Parenthesis with no data.
  2026. $CFG->fullnamedisplay = 'firstname (middlename) lastname';
  2027. $expectedname = "$user->firstname $user->lastname";
  2028. $testname = fullname($user);
  2029. $this->assertSame($expectedname, $testname);
  2030. // Extra spaces due to no data.
  2031. $CFG->fullnamedisplay = 'firstname middlename lastname';
  2032. $expectedname = "$user->firstname $user->lastname";
  2033. $testname = fullname($user);
  2034. $this->assertSame($expectedname, $testname);
  2035. // Regular expression testing.
  2036. // Remove some data from the user fields.
  2037. $user->firstnamephonetic = '';
  2038. $user->lastnamephonetic = '';
  2039. // Removing empty brackets and excess whitespace.
  2040. // All of these configurations should resolve to just firstname lastname.
  2041. $configarray = array();
  2042. $configarray[] = 'firstname lastname [firstnamephonetic lastnamephonetic]';
  2043. $configarray[] = 'firstname lastname \'middlename\'';
  2044. $configarray[] = 'firstname "firstnamephonetic" lastname';
  2045. $configarray[] = 'firstname 「firstnamephonetic」 lastname 「lastnamephonetic」';
  2046. foreach ($configarray as $config) {
  2047. $CFG->fullnamedisplay = $config;
  2048. $expectedname = "$user->firstname $user->lastname";
  2049. $testname = fullname($user);
  2050. $this->assertSame($expectedname, $testname);
  2051. }
  2052. // Check to make sure that other characters are left in place.
  2053. $configarray = array();
  2054. $configarray['0'] = new stdClass();
  2055. $configarray['0']->config = 'lastname firstname, middlename';
  2056. $configarray['0']->expectedname = "$user->lastname $user->firstname,";
  2057. $configarray['1'] = new stdClass();
  2058. $configarray['1']->config = 'lastname firstname + alternatename';
  2059. $configarray['1']->expectedname = "$user->lastname $user->firstname + $user->alternatename";
  2060. $configarray['2'] = new stdClass();
  2061. $configarray['2']->config = 'firstname aka: alternatename';
  2062. $configarray['2']->expectedname = "$user->firstname aka: $user->alternatename";
  2063. $configarray['3'] = new stdClass();
  2064. $configarray['3']->config = 'firstname (alternatename)';
  2065. $configarray['3']->expectedname = "$user->firstname ($user->alternatename)";
  2066. $configarray['4'] = new stdClass();
  2067. $configarray['4']->config = 'firstname [alternatename]';
  2068. $configarray['4']->expectedname = "$user->firstname [$user->alternatename]";
  2069. $configarray['5'] = new stdClass();
  2070. $configarray['5']->config = 'firstname "lastname"';
  2071. $configarray['5']->expectedname = "$user->firstname \"$user->lastname\"";
  2072. foreach ($configarray as $config) {
  2073. $CFG->fullnamedisplay = $config->config;
  2074. $expectedname = $config->expectedname;
  2075. $testname = fullname($user);
  2076. $this->assertSame($expectedname, $testname);
  2077. }
  2078. // Test debugging message displays when
  2079. // fullnamedisplay setting is "normal".
  2080. $CFG->fullnamedisplay = 'firstname lastname';
  2081. unset($user);
  2082. $user = new stdClass();
  2083. $user->firstname = 'Stan';
  2084. $user->lastname = 'Lee';
  2085. $namedisplay = fullname($user);
  2086. $this->assertDebuggingCalled();
  2087. // Tidy up after we finish testing.
  2088. $CFG->fullnamedisplay = $originalcfg->fullnamedisplay;
  2089. }
  2090. public function test_get_all_user_name_fields() {
  2091. $this->resetAfterTest();
  2092. // Additional names in an array.
  2093. $testarray = array('firstnamephonetic' => 'firstnamephonetic',
  2094. 'lastnamephonetic' => 'lastnamephonetic',
  2095. 'middlename' => 'middlename',
  2096. 'alternatename' => 'alternatename',
  2097. 'firstname' => 'firstname',
  2098. 'lastname' => 'lastname');
  2099. $this->assertEquals($testarray, get_all_user_name_fields());
  2100. // Additional names as a string.
  2101. $teststring = 'firstnamephonetic,lastnamephonetic,middlename,alternatename,firstname,lastname';
  2102. $this->assertEquals($teststring, get_all_user_name_fields(true));
  2103. // Additional names as a string with an alias.
  2104. $teststring = 't.firstnamephonetic,t.lastnamephonetic,t.middlename,t.alternatename,t.firstname,t.lastname';
  2105. $this->assertEquals($teststring, get_all_user_name_fields(true, 't'));
  2106. // Additional name fields with a prefix - object.
  2107. $testarray = array('firstnamephonetic' => 'authorfirstnamephonetic',
  2108. 'lastnamephonetic' => 'authorlastnamephonetic',
  2109. 'middlename' => 'authormiddlename',
  2110. 'alternatename' => 'authoralternatename',
  2111. 'firstname' => 'authorfirstname',
  2112. 'lastname' => 'authorlastname');
  2113. $this->assertEquals($testarray, get_all_user_name_fields(false, null, 'author'));
  2114. // Additional name fields with an alias and a title - string.
  2115. $teststring = 'u.firstnamephonetic AS authorfirstnamephonetic,u.lastnamephonetic AS authorlastnamephonetic,u.middlename AS authormiddlename,u.alternatename AS authoralternatename,u.firstname AS authorfirstname,u.lastname AS authorlastname';
  2116. $this->assertEquals($teststring, get_all_user_name_fields(true, 'u', null, 'author'));
  2117. }
  2118. public function test_order_in_string() {
  2119. $this->resetAfterTest();
  2120. // Return an array in an order as they are encountered in a string.
  2121. $valuearray = array('second', 'firsthalf', 'first');
  2122. $formatstring = 'first firsthalf some other text (second)';
  2123. $expectedarray = array('0' => 'first', '6' => 'firsthalf', '33' => 'second');
  2124. $this->assertEquals($expectedarray, order_in_string($valuearray, $formatstring));
  2125. // Try again with a different order for the format.
  2126. $valuearray = array('second', 'firsthalf', 'first');
  2127. $formatstring = 'firsthalf first second';
  2128. $expectedarray = array('0' => 'firsthalf', '10' => 'first', '16' => 'second');
  2129. $this->assertEquals($expectedarray, order_in_string($valuearray, $formatstring));
  2130. // Try again with yet another different order for the format.
  2131. $valuearray = array('second', 'firsthalf', 'first');
  2132. $formatstring = 'start seconds away second firstquater first firsthalf';
  2133. $expectedarray = array('19' => 'second', '38' => 'first', '44' => 'firsthalf');
  2134. $this->assertEquals($expectedarray, order_in_string($valuearray, $formatstring));
  2135. }
  2136. public function test_complete_user_login() {
  2137. global $USER, $DB;
  2138. $this->resetAfterTest();
  2139. $user = $this->getDataGenerator()->create_user();
  2140. $this->setUser(0);
  2141. $sink = $this->redirectEvents();
  2142. $loginuser = clone($user);
  2143. $this->setCurrentTimeStart();
  2144. @complete_user_login($loginuser); // Hide session header errors.
  2145. $this->assertSame($loginuser, $USER);
  2146. $this->assertEquals($user->id, $USER->id);
  2147. $events = $sink->get_events();
  2148. $sink->close();
  2149. $this->assertCount(1, $events);
  2150. $event = reset($events);
  2151. $this->assertInstanceOf('\core\event\user_loggedin', $event);
  2152. $this->assertEquals('user', $event->objecttable);
  2153. $this->assertEquals($user->id, $event->objectid);
  2154. $this->assertEquals(context_system::instance()->id, $event->contextid);
  2155. $user = $DB->get_record('user', array('id'=>$user->id));
  2156. $this->assertTimeCurrent($user->firstaccess);
  2157. $this->assertTimeCurrent($user->lastaccess);
  2158. $this->assertTimeCurrent($USER->firstaccess);
  2159. $this->assertTimeCurrent($USER->lastaccess);
  2160. $this->assertTimeCurrent($USER->currentlogin);
  2161. $this->assertSame(sesskey(), $USER->sesskey);
  2162. $this->assertTimeCurrent($USER->preference['_lastloaded']);
  2163. $this->assertObjectNotHasAttribute('password', $USER);
  2164. $this->assertObjectNotHasAttribute('description', $USER);
  2165. }
  2166. /**
  2167. * Test require_logout.
  2168. */
  2169. public function test_require_logout() {
  2170. $this->resetAfterTest();
  2171. $user = $this->getDataGenerator()->create_user();
  2172. $this->setUser($user);
  2173. $this->assertTrue(isloggedin());
  2174. // Logout user and capture event.
  2175. $sink = $this->redirectEvents();
  2176. require_logout();
  2177. $events = $sink->get_events();
  2178. $sink->close();
  2179. $event = array_pop($events);
  2180. // Check if user is logged out.
  2181. $this->assertFalse(isloggedin());
  2182. // Test Event.
  2183. $this->assertInstanceOf('\core\event\user_loggedout', $event);
  2184. $this->assertSame($user->id, $event->objectid);
  2185. $this->assertSame('user_logout', $event->get_legacy_eventname());
  2186. $this->assertEventLegacyData($user, $event);
  2187. $expectedlogdata = array(SITEID, 'user', 'logout', 'view.php?id='.$event->objectid.'&course='.SITEID, $event->objectid, 0,
  2188. $event->objectid);
  2189. $this->assertEventLegacyLogData($expectedlogdata, $event);
  2190. }
  2191. public function test_email_to_user() {
  2192. global $CFG;
  2193. $this->resetAfterTest();
  2194. $user1 = $this->getDataGenerator()->create_user();
  2195. $user2 = $this->getDataGenerator()->create_user();
  2196. $subject = 'subject';
  2197. $messagetext = 'message text';
  2198. $subject2 = 'subject 2';
  2199. $messagetext2 = 'message text 2';
  2200. $this->assertNotEmpty($CFG->noemailever);
  2201. email_to_user($user1, $user2, $subject, $messagetext);
  2202. $this->assertDebuggingCalled('Not sending email due to $CFG->noemailever config setting');
  2203. unset_config('noemailever');
  2204. email_to_user($user1, $user2, $subject, $messagetext);
  2205. $this->assertDebuggingCalled('Unit tests must not send real emails! Use $this->redirectEmails()');
  2206. $sink = $this->redirectEmails();
  2207. email_to_user($user1, $user2, $subject, $messagetext);
  2208. email_to_user($user2, $user1, $subject2, $messagetext2);
  2209. $this->assertSame(2, $sink->count());
  2210. $result = $sink->get_messages();
  2211. $this->assertCount(2, $result);
  2212. $sink->close();
  2213. $this->assertSame($subject, $result[0]->subject);
  2214. $this->assertSame($messagetext, trim($result[0]->body));
  2215. $this->assertSame($user1->email, $result[0]->to);
  2216. $this->assertSame($user2->email, $result[0]->from);
  2217. $this->assertSame($subject2, $result[1]->subject);
  2218. $this->assertSame($messagetext2, trim($result[1]->body));
  2219. $this->assertSame($user2->email, $result[1]->to);
  2220. $this->assertSame($user1->email, $result[1]->from);
  2221. email_to_user($user1, $user2, $subject, $messagetext);
  2222. $this->assertDebuggingCalled('Unit tests must not send real emails! Use $this->redirectEmails()');
  2223. // Test $CFG->emailonlyfromnoreplyaddress.
  2224. set_config('emailonlyfromnoreplyaddress', 1);
  2225. $this->assertNotEmpty($CFG->emailonlyfromnoreplyaddress);
  2226. $sink = $this->redirectEmails();
  2227. email_to_user($user1, $user2, $subject, $messagetext);
  2228. unset_config('emailonlyfromnoreplyaddress');
  2229. email_to_user($user1, $user2, $subject, $messagetext);
  2230. $result = $sink->get_messages();
  2231. $this->assertEquals($CFG->noreplyaddress, $result[0]->from);
  2232. $this->assertNotEquals($CFG->noreplyaddress, $result[1]->from);
  2233. $sink->close();
  2234. }
  2235. /**
  2236. * Test user_updated event trigger by various apis.
  2237. */
  2238. public function test_user_updated_event() {
  2239. global $DB, $CFG;
  2240. $this->resetAfterTest();
  2241. $user = $this->getDataGenerator()->create_user();
  2242. // Set config to allow email_to_user() to be called.
  2243. $CFG->noemailever = false;
  2244. // Update user password.
  2245. $sink = $this->redirectEvents();
  2246. $sink2 = $this->redirectEmails(); // Make sure we are redirecting emails.
  2247. setnew_password_and_mail($user);
  2248. update_internal_user_password($user, 'randompass');
  2249. $events = $sink->get_events();
  2250. $sink->close();
  2251. $sink2->close();
  2252. // Test updated value.
  2253. $dbuser = $DB->get_record('user', array('id' => $user->id));
  2254. $this->assertSame($user->firstname, $dbuser->firstname);
  2255. $this->assertNotSame('M00dLe@T', $dbuser->password);
  2256. // Test event.
  2257. foreach ($events as $event) {
  2258. $this->assertInstanceOf('\core\event\user_updated', $event);
  2259. $this->assertSame($user->id, $event->objectid);
  2260. $this->assertSame('user_updated', $event->get_legacy_eventname());
  2261. $this->assertEventLegacyData($user, $event);
  2262. $this->assertEquals(context_user::instance($user->id), $event->get_context());
  2263. $expectedlogdata = array(SITEID, 'user', 'update', 'view.php?id='.$user->id, '');
  2264. $this->assertEventLegacyLogData($expectedlogdata, $event);
  2265. }
  2266. }
  2267. /**
  2268. * Test remove_course_content deletes course contents
  2269. * TODO Add asserts to verify other data related to course is deleted as well.
  2270. */
  2271. public function test_remove_course_contents() {
  2272. $this->resetAfterTest();
  2273. $course = $this->getDataGenerator()->create_course();
  2274. $user = $this->getDataGenerator()->create_user();
  2275. $gen = $this->getDataGenerator()->get_plugin_generator('core_notes');
  2276. $note = $gen->create_instance(array('courseid' => $course->id, 'userid' => $user->id));
  2277. $this->assertNotEquals(false, note_load($note->id));
  2278. remove_course_contents($course->id, false);
  2279. $this->assertFalse(note_load($note->id));
  2280. }
  2281. /**
  2282. * Test function username_load_fields_from_object().
  2283. */
  2284. public function test_username_load_fields_from_object() {
  2285. $this->resetAfterTest();
  2286. // This object represents the information returned from an sql query.
  2287. $userinfo = new stdClass();
  2288. $userinfo->userid = 1;
  2289. $userinfo->username = 'loosebruce';
  2290. $userinfo->firstname = 'Bruce';
  2291. $userinfo->lastname = 'Campbell';
  2292. $userinfo->firstnamephonetic = 'ブルース';
  2293. $userinfo->lastnamephonetic = 'カンベッル';
  2294. $userinfo->middlename = '';
  2295. $userinfo->alternatename = '';
  2296. $userinfo->email = '';
  2297. $userinfo->picture = 23;
  2298. $userinfo->imagealt = 'Michael Jordan draining another basket.';
  2299. $userinfo->idnumber = 3982;
  2300. // Just user name fields.
  2301. $user = new stdClass();
  2302. $user = username_load_fields_from_object($user, $userinfo);
  2303. $expectedarray = new stdClass();
  2304. $expectedarray->firstname = 'Bruce';
  2305. $expectedarray->lastname = 'Campbell';
  2306. $expectedarray->firstnamephonetic = 'ブルース';
  2307. $expectedarray->lastnamephonetic = 'カンベッル';
  2308. $expectedarray->middlename = '';
  2309. $expectedarray->alternatename = '';
  2310. $this->assertEquals($user, $expectedarray);
  2311. // User information for showing a picture.
  2312. $user = new stdClass();
  2313. $additionalfields = explode(',', user_picture::fields());
  2314. $user = username_load_fields_from_object($user, $userinfo, null, $additionalfields);
  2315. $user->id = $userinfo->userid;
  2316. $expectedarray = new stdClass();
  2317. $expectedarray->id = 1;
  2318. $expectedarray->firstname = 'Bruce';
  2319. $expectedarray->lastname = 'Campbell';
  2320. $expectedarray->firstnamephonetic = 'ブルース';
  2321. $expectedarray->lastnamephonetic = 'カンベッル';
  2322. $expectedarray->middlename = '';
  2323. $expectedarray->alternatename = '';
  2324. $expectedarray->email = '';
  2325. $expectedarray->picture = 23;
  2326. $expectedarray->imagealt = 'Michael Jordan draining another basket.';
  2327. $this->assertEquals($user, $expectedarray);
  2328. // Alter the userinfo object to have a prefix.
  2329. $userinfo->authorfirstname = 'Bruce';
  2330. $userinfo->authorlastname = 'Campbell';
  2331. $userinfo->authorfirstnamephonetic = 'ブルース';
  2332. $userinfo->authorlastnamephonetic = 'カンベッル';
  2333. $userinfo->authormiddlename = '';
  2334. $userinfo->authorpicture = 23;
  2335. $userinfo->authorimagealt = 'Michael Jordan draining another basket.';
  2336. $userinfo->authoremail = 'test@testing.net';
  2337. // Return an object with user picture information.
  2338. $user = new stdClass();
  2339. $additionalfields = explode(',', user_picture::fields());
  2340. $user = username_load_fields_from_object($user, $userinfo, 'author', $additionalfields);
  2341. $user->id = $userinfo->userid;
  2342. $expectedarray = new stdClass();
  2343. $expectedarray->id = 1;
  2344. $expectedarray->firstname = 'Bruce';
  2345. $expectedarray->lastname = 'Campbell';
  2346. $expectedarray->firstnamephonetic = 'ブルース';
  2347. $expectedarray->lastnamephonetic = 'カンベッル';
  2348. $expectedarray->middlename = '';
  2349. $expectedarray->alternatename = '';
  2350. $expectedarray->email = 'test@testing.net';
  2351. $expectedarray->picture = 23;
  2352. $expectedarray->imagealt = 'Michael Jordan draining another basket.';
  2353. $this->assertEquals($user, $expectedarray);
  2354. }
  2355. }