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