[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/tests/ -> moodlelib_test.php (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1