Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   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   * Collator unit tests.
  19   *
  20   * @package    core
  21   * @category   phpunit
  22   * @copyright  2011 Sam Hemelryk
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  /**
  29   * Unit tests for our utf-8 aware collator which is used for sorting.
  30   *
  31   * @package    core
  32   * @category   phpunit
  33   * @copyright  2011 Sam Hemelryk
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class core_collator_testcase extends advanced_testcase {
  37  
  38      /**
  39       * @var string The initial lang, stored because we change it during testing
  40       */
  41      protected $initiallang = null;
  42  
  43      /**
  44       * @var string The last error that has occurred
  45       */
  46      protected $error = null;
  47  
  48      /**
  49       * Prepares things for this test case.
  50       */
  51      protected function setUp(): void {
  52          global $SESSION;
  53          if (isset($SESSION->lang)) {
  54              $this->initiallang = $SESSION->lang;
  55          }
  56          $SESSION->lang = 'en'; // Make sure we test en language to get consistent results, hopefully all systems have this locale.
  57          if (extension_loaded('intl')) {
  58              $this->error = 'Collation aware sorting not supported';
  59          } else {
  60              $this->error = 'Collation aware sorting not supported, PHP extension "intl" is not available.';
  61          }
  62          parent::setUp();
  63      }
  64  
  65      /**
  66       * Cleans things up after this test case has run.
  67       */
  68      protected function tearDown(): void {
  69          global $SESSION;
  70          parent::tearDown();
  71          if ($this->initiallang !== null) {
  72              $SESSION->lang = $this->initiallang;
  73              $this->initiallang = null;
  74          } else {
  75              unset($SESSION->lang);
  76          }
  77      }
  78  
  79      /**
  80       * Tests the static asort method.
  81       */
  82      public function test_asort() {
  83          $arr = array('b' => 'ab', 1 => 'aa', 0 => 'cc');
  84          $result = core_collator::asort($arr);
  85          $this->assertSame(array('aa', 'ab', 'cc'), array_values($arr));
  86          $this->assertSame(array(1, 'b', 0), array_keys($arr));
  87          $this->assertTrue($result);
  88  
  89          $arr = array('b' => 'ab', 1 => 'aa', 0 => 'cc');
  90          $result = core_collator::asort($arr, core_collator::SORT_STRING);
  91          $this->assertSame(array('aa', 'ab', 'cc'), array_values($arr));
  92          $this->assertSame(array(1, 'b', 0), array_keys($arr));
  93          $this->assertTrue($result);
  94  
  95          $arr = array('b' => 'aac', 1 => 'Aac', 0 => 'cc');
  96          $result = core_collator::asort($arr, (core_collator::SORT_STRING | core_collator::CASE_SENSITIVE));
  97          $this->assertSame(array('Aac', 'aac', 'cc'), array_values($arr));
  98          $this->assertSame(array(1, 'b', 0), array_keys($arr));
  99          $this->assertTrue($result);
 100  
 101          $arr = array('b' => 'a1', 1 => 'a10', 0 => 'a3b');
 102          $result = core_collator::asort($arr);
 103          $this->assertSame(array('a1', 'a10', 'a3b'), array_values($arr));
 104          $this->assertSame(array('b', 1, 0), array_keys($arr));
 105          $this->assertTrue($result);
 106  
 107          $arr = array('b' => 'a1', 1 => 'a10', 0 => 'a3b');
 108          $result = core_collator::asort($arr, core_collator::SORT_NATURAL);
 109          $this->assertSame(array('a1', 'a3b', 'a10'), array_values($arr));
 110          $this->assertSame(array('b', 0, 1), array_keys($arr));
 111          $this->assertTrue($result);
 112  
 113          $arr = array('b' => '1.1.1', 1 => '1.2', 0 => '1.20.2');
 114          $result = core_collator::asort($arr, core_collator::SORT_NATURAL);
 115          $this->assertSame(array_values($arr), array('1.1.1', '1.2', '1.20.2'));
 116          $this->assertSame(array_keys($arr), array('b', 1, 0));
 117          $this->assertTrue($result);
 118  
 119          $arr = array('b' => '-1', 1 => 1000, 0 => -1.2, 3 => 1, 4 => false);
 120          $result = core_collator::asort($arr, core_collator::SORT_NUMERIC);
 121          $this->assertSame(array(-1.2, '-1', false, 1, 1000), array_values($arr));
 122          $this->assertSame(array(0, 'b', 4, 3, 1), array_keys($arr));
 123          $this->assertTrue($result);
 124  
 125          $arr = array('b' => array(1), 1 => array(2, 3), 0 => 1);
 126          $result = core_collator::asort($arr, core_collator::SORT_REGULAR);
 127          $this->assertSame(array(1, array(1), array(2, 3)), array_values($arr));
 128          $this->assertSame(array(0, 'b', 1), array_keys($arr));
 129          $this->assertTrue($result);
 130  
 131          // Test sorting of array of arrays - first element should be used for actual comparison.
 132          $arr = array(0=>array('bb', 'z'), 1=>array('ab', 'a'), 2=>array('zz', 'x'));
 133          $result = core_collator::asort($arr, core_collator::SORT_REGULAR);
 134          $this->assertSame(array(1, 0, 2), array_keys($arr));
 135          $this->assertTrue($result);
 136  
 137          $arr = array('a' => 'áb', 'b' => 'ab', 1 => 'aa', 0=>'cc', 'x' => 'Áb');
 138          $result = core_collator::asort($arr);
 139          $this->assertSame(array('aa', 'ab', 'áb', 'Áb', 'cc'), array_values($arr), $this->error);
 140          $this->assertSame(array(1, 'b', 'a', 'x', 0), array_keys($arr), $this->error);
 141          $this->assertTrue($result);
 142  
 143          $a = array(2=>'b', 1=>'c');
 144          $c =& $a;
 145          $b =& $a;
 146          core_collator::asort($b);
 147          $this->assertSame($a, $b);
 148          $this->assertSame($c, $b);
 149      }
 150  
 151      /**
 152       * Tests the static asort_objects_by_method method.
 153       */
 154      public function test_asort_objects_by_method() {
 155          $objects = array(
 156              'b' => new string_test_class('ab'),
 157              1 => new string_test_class('aa'),
 158              0 => new string_test_class('cc')
 159          );
 160          $result = core_collator::asort_objects_by_method($objects, 'get_protected_name');
 161          $this->assertSame(array(1, 'b', 0), array_keys($objects));
 162          $this->assertSame(array('aa', 'ab', 'cc'), $this->get_ordered_names($objects, 'get_protected_name'));
 163          $this->assertTrue($result);
 164  
 165          $objects = array(
 166              'b' => new string_test_class('a20'),
 167              1 => new string_test_class('a1'),
 168              0 => new string_test_class('a100')
 169          );
 170          $result = core_collator::asort_objects_by_method($objects, 'get_protected_name', core_collator::SORT_NATURAL);
 171          $this->assertSame(array(1, 'b', 0), array_keys($objects));
 172          $this->assertSame(array('a1', 'a20', 'a100'), $this->get_ordered_names($objects, 'get_protected_name'));
 173          $this->assertTrue($result);
 174      }
 175  
 176      /**
 177       * Tests the static asort_objects_by_method method.
 178       */
 179      public function test_asort_objects_by_property() {
 180          $objects = array(
 181              'b' => new string_test_class('ab'),
 182              1 => new string_test_class('aa'),
 183              0 => new string_test_class('cc')
 184          );
 185          $result = core_collator::asort_objects_by_property($objects, 'publicname');
 186          $this->assertSame(array(1, 'b', 0), array_keys($objects));
 187          $this->assertSame(array('aa', 'ab', 'cc'), $this->get_ordered_names($objects, 'publicname'));
 188          $this->assertTrue($result);
 189  
 190          $objects = array(
 191              'b' => new string_test_class('a20'),
 192              1 => new string_test_class('a1'),
 193              0 => new string_test_class('a100')
 194          );
 195          $result = core_collator::asort_objects_by_property($objects, 'publicname', core_collator::SORT_NATURAL);
 196          $this->assertSame(array(1, 'b', 0), array_keys($objects));
 197          $this->assertSame(array('a1', 'a20', 'a100'), $this->get_ordered_names($objects, 'publicname'));
 198          $this->assertTrue($result);
 199      }
 200  
 201      /**
 202       * Tests the sorting of an array of arrays by key.
 203       */
 204      public function test_asort_array_of_arrays_by_key() {
 205          $array = array(
 206              'a' => array('name' => 'bravo'),
 207              'b' => array('name' => 'charlie'),
 208              'c' => array('name' => 'alpha')
 209          );
 210          $this->assertSame(array('a', 'b', 'c'), array_keys($array));
 211          $this->assertTrue(core_collator::asort_array_of_arrays_by_key($array, 'name'));
 212          $this->assertSame(array('c', 'a', 'b'), array_keys($array));
 213  
 214          $array = array(
 215              'a' => array('name' => 'b'),
 216              'b' => array('name' => 1),
 217              'c' => array('name' => 0)
 218          );
 219          $this->assertSame(array('a', 'b', 'c'), array_keys($array));
 220          $this->assertTrue(core_collator::asort_array_of_arrays_by_key($array, 'name'));
 221          $this->assertSame(array('c', 'b', 'a'), array_keys($array));
 222  
 223          $array = array(
 224              'a' => array('name' => 'áb'),
 225              'b' => array('name' => 'ab'),
 226              1   => array('name' => 'aa'),
 227              'd' => array('name' => 'cc'),
 228              0   => array('name' => 'Áb')
 229          );
 230          $this->assertSame(array('a', 'b', 1, 'd', 0), array_keys($array));
 231          $this->assertTrue(core_collator::asort_array_of_arrays_by_key($array, 'name'));
 232          $this->assertSame(array(1, 'b', 'a', 0, 'd'), array_keys($array));
 233          $this->assertSame(array(
 234              1   => array('name' => 'aa'),
 235              'b' => array('name' => 'ab'),
 236              'a' => array('name' => 'áb'),
 237              0   => array('name' => 'Áb'),
 238              'd' => array('name' => 'cc')
 239          ), $array);
 240  
 241      }
 242  
 243      /**
 244       * Returns an array of sorted names.
 245       * @param array $objects
 246       * @param string $methodproperty
 247       * @return array
 248       */
 249      protected function get_ordered_names($objects, $methodproperty = 'get_protected_name') {
 250          $return = array();
 251          foreach ($objects as $object) {
 252              if ($methodproperty == 'publicname') {
 253                  $return[] = $object->publicname;
 254              } else {
 255                  $return[] = $object->$methodproperty();
 256              }
 257          }
 258          return $return;
 259      }
 260  
 261      /**
 262       * Tests the static ksort method.
 263       */
 264      public function test_ksort() {
 265          $arr = array('b' => 'ab', 1 => 'aa', 0 => 'cc');
 266          $result = core_collator::ksort($arr);
 267          $this->assertSame(array(0, 1, 'b'), array_keys($arr));
 268          $this->assertSame(array('cc', 'aa', 'ab'), array_values($arr));
 269          $this->assertTrue($result);
 270  
 271          $obj = new stdClass();
 272          $arr = array('1.1.1'=>array(), '1.2'=>$obj, '1.20.2'=>null);
 273          $result = core_collator::ksort($arr, core_collator::SORT_NATURAL);
 274          $this->assertSame(array('1.1.1', '1.2', '1.20.2'), array_keys($arr));
 275          $this->assertSame(array(array(), $obj, null), array_values($arr));
 276          $this->assertTrue($result);
 277  
 278          $a = array(2=>'b', 1=>'c');
 279          $c =& $a;
 280          $b =& $a;
 281          core_collator::ksort($b);
 282          $this->assertSame($a, $b);
 283          $this->assertSame($c, $b);
 284      }
 285  
 286  }
 287  
 288  
 289  /**
 290   * Simple class used to work with the unit test.
 291   *
 292   * @package    core
 293   * @category   phpunit
 294   * @copyright  2011 Sam Hemelryk
 295   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 296   */
 297  class string_test_class extends stdClass {
 298      /**
 299       * @var string A public property
 300       */
 301      public $publicname;
 302      /**
 303       * @var string A protected property
 304       */
 305      protected $protectedname;
 306      /**
 307       * @var string A private property
 308       */
 309      private $privatename;
 310      /**
 311       * Constructs the test instance.
 312       * @param string $name
 313       */
 314      public function __construct($name) {
 315          $this->publicname = $name;
 316          $this->protectedname = $name;
 317          $this->privatename = $name;
 318      }
 319      /**
 320       * Returns the protected property.
 321       * @return string
 322       */
 323      public function get_protected_name() {
 324          return $this->protectedname;
 325      }
 326      /**
 327       * Returns the protected property.
 328       * @return string
 329       */
 330      public function get_private_name() {
 331          return $this->publicname;
 332      }
 333  }