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_group\reportbuilder\datasource;
  20  
  21  use core_customfield_generator;
  22  use core_reportbuilder_generator;
  23  use core_reportbuilder_testcase;
  24  use core_reportbuilder\local\filters\{boolean_select, date, select, text};
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
  30  
  31  /**
  32   * Unit tests for groups datasource
  33   *
  34   * @package     core_group
  35   * @covers      \core_group\reportbuilder\datasource\groups
  36   * @copyright   2022 Paul Holden <paulh@moodle.com>
  37   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class groups_test extends core_reportbuilder_testcase {
  40  
  41      /**
  42       * Test default datasource
  43       */
  44      public function test_datasource_default(): void {
  45          $this->resetAfterTest();
  46  
  47          $course = $this->getDataGenerator()->create_course();
  48          $userone = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Zoe']);
  49          $usertwo = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Amy']);
  50  
  51          $groupone = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'name' => 'Zebras']);
  52          $grouptwo = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'name' => 'Aardvarks']);
  53  
  54          $this->getDataGenerator()->create_group_member(['groupid' => $groupone->id, 'userid' => $userone->id]);
  55          $this->getDataGenerator()->create_group_member(['groupid' => $groupone->id, 'userid' => $usertwo->id]);
  56  
  57          /** @var core_reportbuilder_generator $generator */
  58          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
  59          $report = $generator->create_report(['name' => 'Groups', 'source' => groups::class, 'default' => 1]);
  60  
  61          $content = $this->get_custom_report_content($report->get('id'));
  62  
  63          // Default columns are course, group, user. Sorted by each.
  64          $courseurl = course_get_url($course);
  65          $this->assertEquals([
  66              ["<a href=\"{$courseurl}\">{$course->fullname}</a>", $grouptwo->name, ''],
  67              ["<a href=\"{$courseurl}\">{$course->fullname}</a>", $groupone->name, fullname($usertwo)],
  68              ["<a href=\"{$courseurl}\">{$course->fullname}</a>", $groupone->name, fullname($userone)],
  69          ], array_map('array_values', $content));
  70      }
  71  
  72      /**
  73       * Test datasource groupings reports
  74       */
  75      public function test_datasource_groupings(): void {
  76          $this->resetAfterTest();
  77  
  78          $course = $this->getDataGenerator()->create_course();
  79  
  80          // Create group, add to grouping.
  81          $groupone = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'name' => 'Group A']);
  82          $grouping = $this->getDataGenerator()->create_grouping(['courseid' => $course->id]);
  83          $this->getDataGenerator()->create_grouping_group(['groupingid' => $grouping->id, 'groupid' => $groupone->id]);
  84  
  85          // Create second group, no grouping.
  86          $grouptwo = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'name' => 'Group B']);
  87  
  88          /** @var core_reportbuilder_generator $generator */
  89          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
  90          $report = $generator->create_report(['name' => 'Groups', 'source' => groups::class, 'default' => 0]);
  91  
  92          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']);
  93          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'grouping:name']);
  94          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:name', 'sortenabled' => 1]);
  95  
  96          $content = $this->get_custom_report_content($report->get('id'));
  97          $this->assertCount(2, $content);
  98  
  99          $this->assertEquals([
 100              [
 101                  $course->fullname,
 102                  $grouping->name,
 103                  $groupone->name,
 104              ],
 105              [
 106                  $course->fullname,
 107                  null,
 108                  $grouptwo->name,
 109              ],
 110          ], array_map('array_values', $content));
 111      }
 112  
 113      /**
 114       * Test datasource columns that aren't added by default
 115       */
 116      public function test_datasource_non_default_columns(): void {
 117          $this->resetAfterTest();
 118          $this->setAdminUser();
 119  
 120          $course = $this->getDataGenerator()->create_course();
 121          $user = $this->getDataGenerator()->create_and_enrol($course, 'student');
 122  
 123          /** @var core_customfield_generator $generator */
 124          $generator = $this->getDataGenerator()->get_plugin_generator('core_customfield');
 125  
 126          // Create group with custom field, and single group member.
 127          $groupfieldcategory = $generator->create_category(['component' => 'core_group', 'area' => 'group']);
 128          $generator->create_field(['categoryid' => $groupfieldcategory->get('id'), 'shortname' => 'hi']);
 129  
 130          $group = $this->getDataGenerator()->create_group([
 131              'courseid' => $course->id,
 132              'idnumber' => 'G101',
 133              'enrolmentkey' => 'S',
 134              'description' => 'My group',
 135              'customfield_hi' => 'Hello',
 136          ]);
 137          $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 138  
 139          // Create grouping with custom field, and single group.
 140          $groupingfieldcategory = $generator->create_category(['component' => 'core_group', 'area' => 'grouping']);
 141          $generator->create_field(['categoryid' => $groupingfieldcategory->get('id'), 'shortname' => 'bye']);
 142  
 143          $grouping = $this->getDataGenerator()->create_grouping([
 144              'courseid' => $course->id,
 145              'idnumber' => 'GR101',
 146              'description' => 'My grouping',
 147              'customfield_bye' => 'Goodbye',
 148          ]);
 149          $this->getDataGenerator()->create_grouping_group(['groupingid' => $grouping->id, 'groupid' => $group->id]);
 150  
 151          /** @var core_reportbuilder_generator $generator */
 152          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
 153          $report = $generator->create_report(['name' => 'Groups', 'source' => groups::class, 'default' => 0]);
 154  
 155          // Course (just to test join).
 156          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:shortname']);
 157  
 158          // Group.
 159          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:idnumber']);
 160          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:description']);
 161          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:enrolmentkey']);
 162          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:visibility']);
 163          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:participation']);
 164          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:picture']);
 165          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:timecreated']);
 166          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:timemodified']);
 167          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:customfield_hi']);
 168  
 169          // Grouping.
 170          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'grouping:name']);
 171          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'grouping:idnumber']);
 172          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'grouping:description']);
 173          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'grouping:timecreated']);
 174          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'grouping:timemodified']);
 175          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'grouping:customfield_bye']);
 176  
 177          // Group member.
 178          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group_member:timeadded']);
 179          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group_member:component']);
 180  
 181          // User (just to test join).
 182          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:username']);
 183  
 184          $content = $this->get_custom_report_content($report->get('id'));
 185          $this->assertCount(1, $content);
 186  
 187          [
 188              $courseshortname,
 189              $groupidnumber,
 190              $groupdescription,
 191              $groupenrolmentkey,
 192              $groupvisibility,
 193              $groupparticipation,
 194              $grouppicture,
 195              $grouptimecreated,
 196              $grouptimemodified,
 197              $groupcustomfield,
 198              $groupingname,
 199              $groupingidnumber,
 200              $groupingdescription,
 201              $groupingtimecreated,
 202              $groupingtimemodified,
 203              $groupingcustomfield,
 204              $groupmembertimeadded,
 205              $groupmemebercomponent,
 206              $userusername,
 207          ] = array_values($content[0]);
 208  
 209          $this->assertEquals($course->shortname, $courseshortname);
 210          $this->assertEquals('G101', $groupidnumber);
 211          $this->assertEquals(format_text($group->description), $groupdescription);
 212          $this->assertEquals('S', $groupenrolmentkey);
 213          $this->assertEquals('Visible', $groupvisibility);
 214          $this->assertEquals('Yes', $groupparticipation);
 215          $this->assertEmpty($grouppicture);
 216          $this->assertNotEmpty($grouptimecreated);
 217          $this->assertNotEmpty($grouptimemodified);
 218          $this->assertEquals('Hello', $groupcustomfield);
 219          $this->assertEquals($grouping->name, $groupingname);
 220          $this->assertEquals('GR101', $groupingidnumber);
 221          $this->assertEquals(format_text($grouping->description), $groupingdescription);
 222          $this->assertNotEmpty($groupingtimecreated);
 223          $this->assertNotEmpty($groupingtimemodified);
 224          $this->assertEquals('Goodbye', $groupingcustomfield);
 225          $this->assertNotEmpty($groupmembertimeadded);
 226          $this->assertEmpty($groupmemebercomponent);
 227          $this->assertEquals($user->username, $userusername);
 228      }
 229  
 230      /**
 231       * Data provider for {@see test_datasource_filters}
 232       *
 233       * @return array[]
 234       */
 235      public function datasource_filters_provider(): array {
 236          return [
 237              // Course (just to test join).
 238              'Filter course name' => ['course:fullname', [
 239                  'course:fullname_operator' => text::IS_EQUAL_TO,
 240                  'course:fullname_value' => 'Test course',
 241              ], true],
 242              'Filter course name (no match)' => ['course:fullname', [
 243                  'course:fullname_operator' => text::IS_NOT_EQUAL_TO,
 244                  'course:fullname_value' => 'Test course',
 245              ], false],
 246  
 247              // Group.
 248              'Filter group name' => ['group:name', [
 249                  'group:name_operator' => text::IS_EQUAL_TO,
 250                  'group:name_value' => 'Test group',
 251              ], true],
 252              'Filter group name (no match)' => ['group:name', [
 253                  'group:name_operator' => text::IS_NOT_EQUAL_TO,
 254                  'group:name_value' => 'Test group',
 255              ], false],
 256              'Filter group idnumber' => ['group:idnumber', [
 257                  'group:idnumber_operator' => text::IS_EQUAL_TO,
 258                  'group:idnumber_value' => 'G101',
 259              ], true],
 260              'Filter group idnumber (no match)' => ['group:idnumber', [
 261                  'group:idnumber_operator' => text::IS_NOT_EQUAL_TO,
 262                  'group:idnumber_value' => 'G101',
 263              ], false],
 264              'Filter group visibility' => ['group:visibility', [
 265                  'group:visibility_operator' => select::EQUAL_TO,
 266                  'group:visibility_value' => 0, // Visible to everyone.
 267              ], true],
 268              'Filter group visibility (no match)' => ['group:visibility', [
 269                  'group:visibility_operator' => select::EQUAL_TO,
 270                  'group:visibility_value' => 1, // Visible to members only.
 271              ], false],
 272              'Filter group participation' => ['group:participation', [
 273                  'group:participation_operator' => boolean_select::CHECKED,
 274              ], true],
 275              'Filter group participation (no match)' => ['group:participation', [
 276                  'group:participation_operator' => boolean_select::NOT_CHECKED,
 277              ], false],
 278              'Filter group time created' => ['group:timecreated', [
 279                  'group:timecreated_operator' => date::DATE_RANGE,
 280                  'group:timecreated_from' => 1622502000,
 281              ], true],
 282              'Filter group time created (no match)' => ['group:timecreated', [
 283                  'group:timecreated_operator' => date::DATE_RANGE,
 284                  'group:timecreated_to' => 1622502000,
 285              ], false],
 286  
 287              // Grouping.
 288              'Filter grouping name' => ['grouping:name', [
 289                  'grouping:name_operator' => text::IS_EQUAL_TO,
 290                  'grouping:name_value' => 'Test grouping',
 291              ], true],
 292              'Filter grouping name (no match)' => ['grouping:name', [
 293                  'grouping:name_operator' => text::IS_NOT_EQUAL_TO,
 294                  'grouping:name_value' => 'Test grouping',
 295              ], false],
 296              'Filter grouping idnumber' => ['grouping:idnumber', [
 297                  'grouping:idnumber_operator' => text::IS_EQUAL_TO,
 298                  'grouping:idnumber_value' => 'GR101',
 299              ], true],
 300              'Filter grouping idnumber (no match)' => ['grouping:idnumber', [
 301                  'grouping:idnumber_operator' => text::IS_NOT_EQUAL_TO,
 302                  'grouping:idnumber_value' => 'GR101',
 303              ], false],
 304              'Filter grouping time created' => ['grouping:timecreated', [
 305                  'grouping:timecreated_operator' => date::DATE_RANGE,
 306                  'grouping:timecreated_from' => 1622502000,
 307              ], true],
 308              'Filter grouping time created (no match)' => ['grouping:timecreated', [
 309                  'grouping:timecreated_operator' => date::DATE_RANGE,
 310                  'grouping:timecreated_to' => 1622502000,
 311              ], false],
 312  
 313              // Group member.
 314              'Filter group member time added' => ['group_member:timeadded', [
 315                  'group_member:timeadded_operator' => date::DATE_RANGE,
 316                  'group_member:timeadded_from' => 1622502000,
 317              ], true],
 318              'Filter group member time added (no match)' => ['group_member:timeadded', [
 319                  'group_member:timeadded_operator' => date::DATE_RANGE,
 320                  'group_member:timeadded_to' => 1622502000,
 321              ], false],
 322  
 323              // User (just to test join).
 324              'Filter user username' => ['user:username', [
 325                  'user:username_operator' => text::IS_EQUAL_TO,
 326                  'user:username_value' => 'testuser',
 327              ], true],
 328              'Filter user username (no match)' => ['user:username', [
 329                  'user:username_operator' => text::IS_NOT_EQUAL_TO,
 330                  'user:username_value' => 'testuser',
 331              ], false],
 332          ];
 333      }
 334  
 335      /**
 336       * Test datasource filters
 337       *
 338       * @param string $filtername
 339       * @param array $filtervalues
 340       * @param bool $expectmatch
 341       *
 342       * @dataProvider datasource_filters_provider
 343       */
 344      public function test_datasource_filters(
 345          string $filtername,
 346          array $filtervalues,
 347          bool $expectmatch
 348      ): void {
 349          $this->resetAfterTest();
 350  
 351          $course = $this->getDataGenerator()->create_course(['fullname' => 'Test course']);
 352          $user = $this->getDataGenerator()->create_and_enrol($course, 'student', ['username' => 'testuser']);
 353  
 354          $group = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'idnumber' => 'G101', 'name' => 'Test group']);
 355          $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 356  
 357          $grouping = $this->getDataGenerator()->create_grouping(['courseid' => $course->id, 'idnumber' => 'GR101',
 358              'name' => 'Test grouping']);
 359          $this->getDataGenerator()->create_grouping_group(['groupingid' => $grouping->id, 'groupid' => $group->id]);
 360  
 361          /** @var core_reportbuilder_generator $generator */
 362          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
 363  
 364          // Create report containing single column, and given filter.
 365          $report = $generator->create_report(['name' => 'Tasks', 'source' => groups::class, 'default' => 0]);
 366          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'group:idnumber']);
 367  
 368          // Add filter, set it's values.
 369          $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
 370          $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues);
 371  
 372          if ($expectmatch) {
 373              $this->assertCount(1, $content);
 374              $this->assertEquals('G101', reset($content[0]));
 375          } else {
 376              $this->assertEmpty($content);
 377          }
 378      }
 379  
 380      /**
 381       * Stress test datasource
 382       *
 383       * In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
 384       */
 385      public function test_stress_datasource(): void {
 386          if (!PHPUNIT_LONGTEST) {
 387              $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
 388          }
 389  
 390          $this->resetAfterTest();
 391  
 392          $course = $this->getDataGenerator()->create_course(['fullname' => 'Test course']);
 393          $user = $this->getDataGenerator()->create_and_enrol($course, 'student', ['username' => 'testuser']);
 394  
 395          $group = $this->getDataGenerator()->create_group(['courseid' => $course->id, 'idnumber' => 'G101', 'name' => 'Test group']);
 396          $this->getDataGenerator()->create_group_member(['userid' => $user->id, 'groupid' => $group->id]);
 397  
 398          $grouping = $this->getDataGenerator()->create_grouping(['courseid' => $course->id, 'idnumber' => 'GR101',
 399              'name' => 'Test grouping']);
 400          $this->getDataGenerator()->create_grouping_group(['groupingid' => $grouping->id, 'groupid' => $group->id]);
 401  
 402          $this->datasource_stress_test_columns(groups::class);
 403          $this->datasource_stress_test_columns_aggregation(groups::class);
 404          $this->datasource_stress_test_conditions(groups::class, 'group:idnumber');
 405      }
 406  }