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]

   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  declare(strict_types=1);
  18  
  19  namespace core_table\local\filter;
  20  
  21  use advanced_testcase;
  22  use InvalidArgumentException;
  23  
  24  /**
  25   * Unit tests for core_table\local\filter\filter.
  26   *
  27   * @package   core_table
  28   * @category  test
  29   * @copyright 2020 Andrew Nicols <andrew@nicols.co.uk>
  30   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31   * @covers    \core_table\local\filter\filter
  32   */
  33  class filter_test extends advanced_testcase {
  34      /**
  35       * Test that the constructor correctly handles a number of conditions.
  36       *
  37       * @dataProvider constructor_provider
  38       * @param array $args
  39       * @param int $jointype
  40       * @param array $values
  41       */
  42      public function test_constructor(array $args, int $jointype, array $values): void {
  43          $filter = new filter(...$args);
  44  
  45          // We should always get a filter.
  46          $this->assertInstanceOf(filter::class, $filter);
  47  
  48          // We should always get the correct join type.
  49          $this->assertEquals($jointype, $filter->get_join_type());
  50  
  51          // The values should be the expected ones.
  52          $this->assertSame($values, $filter->get_filter_values());
  53      }
  54  
  55      /**
  56       * Data provider for the constructor providing a range of valid constructor arguments.
  57       *
  58       * @return array
  59       */
  60      public function constructor_provider(): array {
  61          return [
  62              'Name without values' => [['keyword'], filter::JOINTYPE_DEFAULT, []],
  63              'Name with valid join type ANY' => [[
  64                  'keyword',
  65                  filter::JOINTYPE_ANY,
  66              ], filter::JOINTYPE_ANY, []],
  67              'Name with valid join type ALL' => [[
  68                  'keyword',
  69                  filter::JOINTYPE_ALL,
  70              ], filter::JOINTYPE_ALL, []],
  71              'Name with valid join type NONE' => [[
  72                  'keyword',
  73                  filter::JOINTYPE_NONE,
  74              ], filter::JOINTYPE_NONE, []],
  75              'Name, no join type, with set of values' => [
  76                  [
  77                      'keyword',
  78                      null,
  79                      [
  80                          's1',
  81                          'janine',
  82                      ],
  83                  ],
  84                  filter::JOINTYPE_DEFAULT,
  85                  [
  86                      'janine',
  87                      's1',
  88                  ],
  89              ],
  90              'Name, and ANY, with set of values' => [
  91                  [
  92                      'keyword',
  93                      filter::JOINTYPE_ANY,
  94                      [
  95                          's1',
  96                          'kevin',
  97                          'james',
  98                          'janine',
  99                      ],
 100                  ],
 101                  filter::JOINTYPE_ANY,
 102                  [
 103                      'james',
 104                      'janine',
 105                      'kevin',
 106                      's1',
 107                  ],
 108              ],
 109              'Name, and ANY, with set of values which contains duplicates' => [
 110                  [
 111                      'keyword',
 112                      filter::JOINTYPE_ANY,
 113                      [
 114                          's1',
 115                          'kevin',
 116                          'james',
 117                          'janine',
 118                          'kevin',
 119                      ],
 120                  ],
 121                  filter::JOINTYPE_ANY,
 122                  [
 123                      'james',
 124                      'janine',
 125                      'kevin',
 126                      's1',
 127                  ],
 128              ],
 129          ];
 130      }
 131  
 132      /**
 133       * Test that the constructor throws a relevant exception when passed an invalid join.
 134       *
 135       * @dataProvider constructor_invalid_join_provider
 136       * @param mixed $jointype
 137       */
 138      public function test_constructor_invalid_joins($jointype): void {
 139          $this->expectException(InvalidArgumentException::class);
 140          $this->expectExceptionMessage('Invalid join type specified');
 141  
 142          new filter('invalid', $jointype);
 143      }
 144  
 145      /**
 146       * Data provider for the constructor providing a range of invalid join types to the constructor.
 147       *
 148       * @return array
 149       */
 150      public function constructor_invalid_join_provider(): array {
 151          return [
 152              'Too low' => [-1],
 153              'Too high' => [4],
 154          ];
 155      }
 156  
 157      /**
 158       * Enusre that adding filter values works as expected.
 159       */
 160      public function test_add_filter_value(): void {
 161          $filter = new filter('example');
 162  
 163          // Initially an empty list.
 164          $this->assertEmpty($filter->get_filter_values());
 165  
 166          // Adding null should do nothing.
 167          $filter->add_filter_value(null);
 168          $this->assertEmpty($filter->get_filter_values());
 169  
 170          // Adding empty string should do nothing.
 171          $filter->add_filter_value('');
 172          $this->assertEmpty($filter->get_filter_values());
 173  
 174          // Adding a value should return that value.
 175          $filter->add_filter_value('rosie');
 176          $this->assertSame([
 177              'rosie',
 178          ], $filter->get_filter_values());
 179  
 180          // Adding a second value should add that value.
 181          // The values should sorted.
 182          $filter->add_filter_value('arthur');
 183          $this->assertSame([
 184              'arthur',
 185              'rosie',
 186          ], $filter->get_filter_values());
 187  
 188          // Adding a duplicate value should not lead to that value being added again.
 189          $filter->add_filter_value('arthur');
 190          $this->assertSame([
 191              'arthur',
 192              'rosie',
 193          ], $filter->get_filter_values());
 194      }
 195  
 196      /**
 197       * Ensure that it is possibly to set the join type.
 198       */
 199      public function test_set_join_type(): void {
 200          $filter = new filter('example');
 201  
 202          // Initial set with the default type should just work.
 203          // The setter should be chainable.
 204          $this->assertEquals($filter, $filter->set_join_type(filter::JOINTYPE_DEFAULT));
 205          $this->assertEquals(filter::JOINTYPE_DEFAULT, $filter->get_join_type());
 206  
 207          // It should be possible to update the join type later.
 208          $this->assertEquals($filter, $filter->set_join_type(filter::JOINTYPE_NONE));
 209          $this->assertEquals(filter::JOINTYPE_NONE, $filter->get_join_type());
 210  
 211          $this->assertEquals($filter, $filter->set_join_type(filter::JOINTYPE_ANY));
 212          $this->assertEquals(filter::JOINTYPE_ANY, $filter->get_join_type());
 213  
 214          $this->assertEquals($filter, $filter->set_join_type(filter::JOINTYPE_ALL));
 215          $this->assertEquals(filter::JOINTYPE_ALL, $filter->get_join_type());
 216      }
 217  
 218      /**
 219       * Ensure that it is not possible to provide a value out of bounds when setting the join type.
 220       */
 221      public function test_set_join_type_invalid_low(): void {
 222          $filter = new filter('example');
 223  
 224          // Valid join types are current 0, 1, or 2.
 225          // A value too low should be rejected.
 226          $this->expectException(InvalidArgumentException::class);
 227          $this->expectExceptionMessage("Invalid join type specified");
 228          $filter->set_join_type(-1);
 229      }
 230  
 231      /**
 232       * Ensure that it is not possible to provide a value out of bounds when setting the join type.
 233       */
 234      public function test_set_join_type_invalid_high(): void {
 235          $filter = new filter('example');
 236  
 237          // Valid join types are current 0, 1, or 2.
 238          // A value too low should be rejected.
 239          $this->expectException(InvalidArgumentException::class);
 240          $this->expectExceptionMessage("Invalid join type specified");
 241          $filter->set_join_type(4);
 242      }
 243  
 244      /**
 245       * Ensure that the name getter is callable.
 246       */
 247      public function test_get_name(): void {
 248          $filter = new filter('examplename');
 249  
 250          $this->assertEquals('examplename', $filter->get_name());
 251      }
 252  
 253      /**
 254       * Data provider for the countable tests.
 255       *
 256       * @return array
 257       */
 258      public function filter_value_provider(): array {
 259          return [
 260              'Empty' => [[], 0],
 261              'Single value' => [[10], 1],
 262              'Single repeated value' => [[10, 10, 10, 10], 1],
 263              'Multiple values, no repeats' => [[1, 2, 3, 4, 5], 5],
 264              'Multiple values, including repeats' => [[1, 2, 1, 3, 1, 3, 4, 1, 5], 5],
 265          ];
 266      }
 267  
 268      /**
 269       * Ensure that the filter is countable.
 270       *
 271       * @dataProvider    filter_value_provider
 272       * @param   array   $values List of context IDs
 273       * @param   int     $count Expected count
 274       */
 275      public function test_countable($values, $count): void {
 276          $filter = new filter('example', null, $values);
 277  
 278          $this->assertCount($count, $filter);
 279      }
 280  
 281      /**
 282       * Ensure that the contextlist_base iterates over the set of contexts.
 283       */
 284      public function test_filter_iteration(): void {
 285          $filter = new filter('example');
 286  
 287          // The iterator position should be at the start.
 288          $this->assertEquals(0, $filter->key());
 289  
 290          foreach ($filter as $filtervalue) {
 291              // This should not be called.
 292              $this->assertFalse(true);
 293          }
 294  
 295          // The iterator position should still be at the start.
 296          $this->assertEquals(0, $filter->key());
 297  
 298          // Adding filter values should cause the values in the Iterator to be sorted.
 299          $filter = new filter('example');
 300          $filter->add_filter_value(6);
 301          $filter->add_filter_value(5);
 302          $filter->add_filter_value(4);
 303          $filter->add_filter_value(3);
 304          $filter->add_filter_value(2);
 305  
 306          // The iterator position should be at the start after adding values.
 307          $this->assertEquals(0, $filter->key());
 308  
 309          $foundvalues = [];
 310          foreach ($filter as $filtervalue) {
 311              $foundvalues[] = $filtervalue;
 312          }
 313  
 314          $this->assertEquals([2, 3, 4, 5, 6], $foundvalues);
 315  
 316          // The iterator position should now be at position 5.
 317          // The position is automatically updated prior to moving.
 318          $this->assertEquals(5, $filter->key());
 319  
 320          // Adding another value shoudl cause the Iterator to be re-sorted.
 321          $filter->add_filter_value(1);
 322  
 323          // The iterator position should be at the start after adding values.
 324          $this->assertEquals(0, $filter->key());
 325  
 326          $foundvalues = [];
 327          foreach ($filter as $filtervalue) {
 328              $foundvalues[] = $filtervalue;
 329          }
 330  
 331          $this->assertEquals([1, 2, 3, 4, 5, 6], $foundvalues);
 332  
 333          // The iterator position should now be at position 6.
 334          $this->assertEquals(6, $filter->key());
 335      }
 336  
 337      /**
 338       * Tests for the count function of a filter.
 339       */
 340      public function test_filter_current(): void {
 341          $filter = new filter('example', null, [42]);
 342          $this->assertEquals(42, $filter->current());
 343      }
 344  }