Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

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