Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 401 and 403] [Versions 402 and 403]

   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_tag\reportbuilder\datasource;
  20  
  21  use context_course;
  22  use context_user;
  23  use core_reportbuilder_generator;
  24  use core_reportbuilder_testcase;
  25  use core_reportbuilder\local\filters\{boolean_select, date, select};
  26  use core_reportbuilder\local\filters\tags as tags_filter;
  27  
  28  defined('MOODLE_INTERNAL') || die();
  29  
  30  global $CFG;
  31  require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
  32  
  33  /**
  34   * Unit tests for tags datasource
  35   *
  36   * @package     core_tag
  37   * @covers      \core_tag\reportbuilder\datasource\tags
  38   * @copyright   2022 Paul Holden <paulh@moodle.com>
  39   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   */
  41  class tags_test extends core_reportbuilder_testcase {
  42  
  43      /**
  44       * Test default datasource
  45       */
  46      public function test_datasource_default(): void {
  47          $this->resetAfterTest();
  48  
  49          $user = $this->getDataGenerator()->create_user(['interests' => ['Pies']]);
  50          $usercontext = context_user::instance($user->id);
  51  
  52          $course = $this->getDataGenerator()->create_course(['tags' => ['Horses']]);
  53          $coursecontext = context_course::instance($course->id);
  54  
  55          /** @var core_reportbuilder_generator $generator */
  56          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
  57          $report = $generator->create_report(['name' => 'Notes', 'source' => tags::class, 'default' => 1]);
  58  
  59          $content = $this->get_custom_report_content($report->get('id'));
  60          $this->assertCount(2, $content);
  61  
  62          // Default columns are collection, tag (with link), standard, context. Sorted by collection and tag.
  63          [$collection, $tag, $standard, $context] = array_values($content[0]);
  64          $this->assertEquals('Default collection', $collection);
  65          $this->assertStringContainsString('Horses', $tag);
  66          $this->assertEquals('No', $standard);
  67          $this->assertEquals($coursecontext->get_context_name(), $context);
  68  
  69          [$collection, $tag, $standard, $context] = array_values($content[1]);
  70          $this->assertEquals('Default collection', $collection);
  71          $this->assertStringContainsString('Pies', $tag);
  72          $this->assertEquals('No', $standard);
  73          $this->assertEquals($usercontext->get_context_name(), $context);
  74      }
  75  
  76      /**
  77       * Test datasource columns that aren't added by default
  78       */
  79      public function test_datasource_non_default_columns(): void {
  80          $this->resetAfterTest();
  81  
  82          $this->getDataGenerator()->create_tag(['name' => 'Horses', 'description' => 'Neigh', 'flag' => 2]);
  83          $course = $this->getDataGenerator()->create_course(['tags' => ['Horses']]);
  84          $coursecontext = context_course::instance($course->id);
  85  
  86          /** @var core_reportbuilder_generator $generator */
  87          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
  88          $report = $generator->create_report(['name' => 'Notes', 'source' => tags::class, 'default' => 0]);
  89  
  90          // Collection.
  91          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'collection:default']);
  92          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'collection:component']);
  93          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'collection:searchable']);
  94          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'collection:customurl']);
  95  
  96          // Tag.
  97          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:name']);
  98          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:description']);
  99          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:flagged']);
 100          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:timemodified']);
 101  
 102          // Context.
 103          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'context:link']);
 104  
 105          // Instance.
 106          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'instance:area']);
 107          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'instance:component']);
 108          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'instance:itemtype']);
 109          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'instance:itemid']);
 110          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'instance:timecreated']);
 111          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'instance:timemodified']);
 112  
 113          $content = $this->get_custom_report_content($report->get('id'));
 114          $this->assertCount(1, $content);
 115  
 116          $courserow = array_values($content[0]);
 117  
 118          // Collection.
 119          $this->assertEquals('Yes', $courserow[0]);
 120          $this->assertEmpty($courserow[1]);
 121          $this->assertEquals('Yes', $courserow[2]);
 122          $this->assertEmpty($courserow[3]);
 123  
 124          // Tag.
 125          $this->assertEquals('Horses', $courserow[4]);
 126          $this->assertEquals('<div class="text_to_html">Neigh</div>', $courserow[5]);
 127          $this->assertEquals('Yes', $courserow[6]);
 128          $this->assertNotEmpty($courserow[7]);
 129  
 130          // Instance.
 131          $this->assertEquals('<a href="' . $coursecontext->get_url()  . '">' . $coursecontext->get_context_name()  . '</a>',
 132              $courserow[8]);
 133          $this->assertEquals('Courses', $courserow[9]);
 134          $this->assertEquals('core', $courserow[10]);
 135          $this->assertEquals('course', $courserow[11]);
 136          $this->assertEquals($course->id, $courserow[12]);
 137          $this->assertNotEmpty($courserow[13]);
 138          $this->assertNotEmpty($courserow[14]);
 139      }
 140  
 141      /**
 142       * Data provider for {@see test_datasource_filters}
 143       *
 144       * @return array[]
 145       */
 146      public function datasource_filters_provider(): array {
 147          return [
 148              // Collection.
 149              'Filter collection name' => ['collection:name', [
 150                  'collection:name_operator' => select::NOT_EQUAL_TO,
 151                  'collection:name_value' => -1,
 152              ], true],
 153              'Filter collection default' => ['collection:default', [
 154                  'collection:default_operator' => boolean_select::CHECKED,
 155              ], true],
 156              'Filter collection default (no match)' => ['collection:default', [
 157                  'collection:default_operator' => boolean_select::NOT_CHECKED,
 158              ], false],
 159              'Filter collection searchable' => ['collection:searchable', [
 160                  'collection:searchable_operator' => boolean_select::CHECKED,
 161              ], true],
 162              'Filter collection searchable (no match)' => ['collection:searchable', [
 163                  'collection:searchable_operator' => boolean_select::NOT_CHECKED,
 164              ], false],
 165  
 166              // Tag.
 167              'Filter tag name equal to' => ['tag:name', [
 168                  'tag:name_operator' => tags_filter::EQUAL_TO,
 169                  'tag:name_value' => [-1],
 170              ], false],
 171              'Filter tag name not equal to' => ['tag:name', [
 172                  'tag:name_operator' => tags_filter::NOT_EQUAL_TO,
 173                  'tag:name_value' => [-1],
 174              ], true],
 175              'Filter tag name empty' => ['tag:name', [
 176                  'tag:name_operator' => tags_filter::EMPTY,
 177              ], false],
 178              'Filter tag name not empty' => ['tag:name', [
 179                  'tag:name_operator' => tags_filter::NOT_EMPTY,
 180              ], true],
 181              'Filter tag standard' => ['tag:standard', [
 182                  'tag:standard_operator' => boolean_select::NOT_CHECKED,
 183              ], true],
 184              'Filter tag standard (no match)' => ['tag:standard', [
 185                  'tag:standard_operator' => boolean_select::CHECKED,
 186              ], false],
 187              'Filter tag flagged' => ['tag:flagged', [
 188                  'tag:flagged_operator' => boolean_select::CHECKED,
 189              ], true],
 190              'Filter tag flagged (no match)' => ['tag:flagged', [
 191                  'tag:flagged_operator' => boolean_select::NOT_CHECKED,
 192              ], false],
 193              'Filter tag time modified' => ['tag:timemodified', [
 194                  'tag:timemodified_operator' => date::DATE_RANGE,
 195                  'tag:timemodified_from' => 1622502000,
 196              ], true],
 197              'Filter tag time modified (no match)' => ['tag:timemodified', [
 198                  'tag:timemodified_operator' => date::DATE_RANGE,
 199                  'tag:timemodified_to' => 1622502000,
 200              ], false],
 201  
 202              // Instance.
 203              'Filter instance tag area' => ['instance:area', [
 204                  'instance:area_operator' => select::EQUAL_TO,
 205                  'instance:area_value' => 'core/course',
 206              ], true],
 207              'Filter instance tag area (no match)' => ['instance:area', [
 208                  'instance:area_operator' => select::NOT_EQUAL_TO,
 209                  'instance:area_value' => 'core/course',
 210              ], false],
 211              'Filter instance time created' => ['instance:timecreated', [
 212                  'instance:timecreated_operator' => date::DATE_RANGE,
 213                  'instance:timecreated_from' => 1622502000,
 214              ], true],
 215              'Filter instance time created (no match)' => ['instance:timecreated', [
 216                  'instance:timecreated_operator' => date::DATE_RANGE,
 217                  'instance:timecreated_to' => 1622502000,
 218              ], false],
 219              'Filter instance time modified' => ['instance:timemodified', [
 220                  'instance:timemodified_operator' => date::DATE_RANGE,
 221                  'instance:timemodified_from' => 1622502000,
 222              ], true],
 223              'Filter instance time modified (no match)' => ['instance:timemodified', [
 224                  'instance:timemodified_operator' => date::DATE_RANGE,
 225                  'instance:timemodified_to' => 1622502000,
 226              ], false],
 227          ];
 228      }
 229  
 230      /**
 231       * Test datasource filters
 232       *
 233       * @param string $filtername
 234       * @param array $filtervalues
 235       * @param bool $expectmatch
 236       *
 237       * @dataProvider datasource_filters_provider
 238       */
 239      public function test_datasource_filters(
 240          string $filtername,
 241          array $filtervalues,
 242          bool $expectmatch
 243      ): void {
 244          $this->resetAfterTest();
 245  
 246          $this->getDataGenerator()->create_tag(['name' => 'Horses', 'flag' => 2]);
 247          $this->getDataGenerator()->create_course(['tags' => ['Horses']]);
 248  
 249          /** @var core_reportbuilder_generator $generator */
 250          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
 251  
 252          // Create report containing single tag name, and given filter.
 253          $report = $generator->create_report(['name' => 'Tasks', 'source' => tags::class, 'default' => 0]);
 254          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:name']);
 255  
 256          // Add filter, set it's values.
 257          $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
 258          $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues);
 259  
 260          if ($expectmatch) {
 261              $this->assertCount(1, $content);
 262              $this->assertEquals('Horses', reset($content[0]));
 263          } else {
 264              $this->assertEmpty($content);
 265          }
 266      }
 267  
 268      /**
 269       * Stress test datasource
 270       *
 271       * In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
 272       */
 273      public function test_stress_datasource(): void {
 274          if (!PHPUNIT_LONGTEST) {
 275              $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
 276          }
 277  
 278          $this->resetAfterTest();
 279  
 280          $this->getDataGenerator()->create_course(['tags' => ['Horses']]);
 281  
 282          $this->datasource_stress_test_columns(tags::class);
 283          $this->datasource_stress_test_columns_aggregation(tags::class);
 284          $this->datasource_stress_test_conditions(tags::class, 'tag:name');
 285      }
 286  }