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.
   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_course\reportbuilder\datasource;
  20  
  21  use core_reportbuilder_generator;
  22  use core_reportbuilder_testcase;
  23  use core_reportbuilder\local\filters\{select, text};
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  global $CFG;
  28  require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
  29  
  30  /**
  31   * Unit tests for course categories datasource
  32   *
  33   * @package     core_course
  34   * @covers      \core_course\reportbuilder\datasource\categories
  35   * @copyright   2023 Paul Holden <paulh@moodle.com>
  36   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  37   */
  38  class categories_test extends core_reportbuilder_testcase {
  39  
  40      /**
  41       * Test default datasource
  42       */
  43      public function test_datasource_default(): void {
  44          $this->resetAfterTest();
  45  
  46          $category = $this->getDataGenerator()->create_category(['name' => 'Zoo', 'idnumber' => 'Z01']);
  47          $course = $this->getDataGenerator()->create_course(['category' => $category->id]);
  48  
  49          /** @var core_reportbuilder_generator $generator */
  50          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
  51          $report = $generator->create_report(['name' => 'My report', 'source' => categories::class, 'default' => 1]);
  52  
  53          $content = $this->get_custom_report_content($report->get('id'));
  54          $this->assertCount(2, $content);
  55  
  56          // Default columns are name, idnumber, coursecount. Sorted by name ascending.
  57          $this->assertEquals([
  58              [get_string('defaultcategoryname'), '', 0],
  59              [$category->get_formatted_name(), $category->idnumber, 1],
  60          ], array_map('array_values', $content));
  61      }
  62  
  63      /**
  64       * Test datasource columns that aren't added by default
  65       */
  66      public function test_datasource_non_default_columns(): void {
  67          global $DB;
  68  
  69          $this->resetAfterTest();
  70  
  71          $category = $this->getDataGenerator()->create_category(['name' => 'Zoo', 'idnumber' => 'Z01', 'description' => 'Animals']);
  72          $course = $this->getDataGenerator()->create_course(['category' => $category->id, 'fullname' => 'Zebra']);
  73  
  74          // Add a cohort.
  75          $cohort = $this->getDataGenerator()->create_cohort(['contextid' => $category->get_context()->id, 'name' => 'My cohort']);
  76  
  77          // Assign a role.
  78          $managerrole = $DB->get_field('role', 'id', ['shortname' => 'manager']);
  79          $user = $this->getDataGenerator()->create_user();
  80          role_assign($managerrole, $user->id, $category->get_context()->id);
  81  
  82          /** @var core_reportbuilder_generator $generator */
  83          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
  84          $report = $generator->create_report(['name' => 'My report', 'source' => categories::class, 'default' => 0]);
  85  
  86          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course_category:namewithlink',
  87              'sortenabled' => 1]);
  88          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course_category:path']);
  89          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course_category:description']);
  90  
  91          // Add column from each of our entities.
  92          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course:fullname']);
  93          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'cohort:name']);
  94          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'role:name']);
  95          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullname']);
  96  
  97          $content = $this->get_custom_report_content($report->get('id'));
  98          $this->assertCount(2, $content);
  99  
 100          [$namewithlink, $path, $description, $coursename, $cohortname, $rolename, $userfullname] = array_values($content[0]);
 101          $this->assertStringContainsString(get_string('defaultcategoryname'), $namewithlink);
 102          $this->assertEquals(get_string('defaultcategoryname'), $path);
 103          $this->assertEmpty($description);
 104          $this->assertEmpty($coursename);
 105          $this->assertEmpty($cohortname);
 106          $this->assertEmpty($rolename);
 107          $this->assertEmpty($userfullname);
 108  
 109          [$namewithlink, $path, $description, $coursename, $cohortname, $rolename, $userfullname] = array_values($content[1]);
 110          $this->assertStringContainsString($category->get_formatted_name(), $namewithlink);
 111          $this->assertEquals($category->get_nested_name(false), $path);
 112          $this->assertEquals(format_text($category->description, $category->descriptionformat), $description);
 113          $this->assertEquals($course->fullname, $coursename);
 114          $this->assertEquals($cohort->name, $cohortname);
 115          $this->assertEquals('Manager', $rolename);
 116          $this->assertEquals(fullname($user), $userfullname);
 117      }
 118  
 119      /**
 120       * Data provider for {@see test_datasource_filters}
 121       *
 122       * @return array[]
 123       */
 124      public static function datasource_filters_provider(): array {
 125          global $DB;
 126  
 127          return [
 128              // Category.
 129              'Filter category (no match)' => ['course_category:name', [
 130                  'course_category:name_value' => -1,
 131              ], false],
 132              'Filter category name' => ['course_category:text', [
 133                  'course_category:text_operator' => text::IS_EQUAL_TO,
 134                  'course_category:text_value' => 'Zoo',
 135              ], true],
 136              'Filter category name (no match)' => ['course_category:text', [
 137                  'course_category:text_operator' => text::IS_EQUAL_TO,
 138                  'course_category:text_value' => 'Plants',
 139              ], false],
 140              'Filter category idnumber' => ['course_category:idnumber', [
 141                  'course_category:idnumber_operator' => text::IS_EQUAL_TO,
 142                  'course_category:idnumber_value' => 'Z01',
 143              ], true],
 144              'Filter category idnumber (no match)' => ['course_category:idnumber', [
 145                  'course_category:idnumber_operator' => text::IS_EQUAL_TO,
 146                  'course_category:idnumber_value' => 'P01',
 147              ], false],
 148  
 149              // Course.
 150              'Filter course fullname' => ['course:fullname', [
 151                  'course:fullname_operator' => text::IS_EQUAL_TO,
 152                  'course:fullname_value' => 'Zebra',
 153              ], true],
 154              'Filter course fullname (no match)' => ['course:fullname', [
 155                  'course:fullname_operator' => text::IS_EQUAL_TO,
 156                  'course:fullname_value' => 'Python',
 157              ], false],
 158  
 159              // Cohort.
 160              'Filter cohort name' => ['cohort:name', [
 161                  'cohort:name_operator' => text::IS_EQUAL_TO,
 162                  'cohort:name_value' => 'My cohort',
 163              ], true],
 164              'Filter cohort name (no match)' => ['cohort:name', [
 165                  'cohort:name_operator' => text::IS_EQUAL_TO,
 166                  'cohort:name_value' => 'Not my cohort',
 167              ], false],
 168  
 169              // Role.
 170              'Filter role' => ['role:name', [
 171                  'role:name_operator' => select::EQUAL_TO,
 172                  'role:name_value' => $DB->get_field('role', 'id', ['shortname' => 'manager']),
 173              ], true],
 174              'Filter role (no match)' => ['role:name', [
 175                  'role:name_operator' => select::EQUAL_TO,
 176                  'role:name_value' => -1,
 177              ], false],
 178  
 179              // User.
 180              'Filter user firstname' => ['user:firstname', [
 181                  'user:firstname_operator' => text::IS_EQUAL_TO,
 182                  'user:firstname_value' => 'Zoe',
 183              ], true],
 184              'Filter user firstname (no match)' => ['user:firstname', [
 185                  'user:firstname_operator' => text::IS_EQUAL_TO,
 186                  'user:firstname_value' => 'Pedro',
 187              ], false],
 188          ];
 189      }
 190  
 191      /**
 192       * Test datasource filters
 193       *
 194       * @param string $filtername
 195       * @param array $filtervalues
 196       * @param bool $expectmatch
 197       *
 198       * @dataProvider datasource_filters_provider
 199       */
 200      public function test_datasource_filters(string $filtername, array $filtervalues, bool $expectmatch): void {
 201          global $DB;
 202  
 203          $this->resetAfterTest();
 204  
 205          $category = $this->getDataGenerator()->create_category(['name' => 'Zoo', 'idnumber' => 'Z01']);
 206          $course = $this->getDataGenerator()->create_course(['category' => $category->id, 'fullname' => 'Zebra']);
 207  
 208          // Add a cohort.
 209          $cohort = $this->getDataGenerator()->create_cohort(['contextid' => $category->get_context()->id, 'name' => 'My cohort']);
 210  
 211          // Assign a role.
 212          $managerrole = $DB->get_field('role', 'id', ['shortname' => 'manager']);
 213          $user = $this->getDataGenerator()->create_user(['firstname' => 'Zoe']);
 214          role_assign($managerrole, $user->id, $category->get_context()->id);
 215  
 216          /** @var core_reportbuilder_generator $generator */
 217          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
 218  
 219          // Create report containing single idnumber column, and given filter.
 220          $report = $generator->create_report(['name' => 'My report', 'source' => categories::class, 'default' => 0]);
 221          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'course_category:idnumber']);
 222  
 223          // Add filter, set it's values.
 224          $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
 225          $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues);
 226  
 227          if ($expectmatch) {
 228              $this->assertCount(1, $content);
 229              $this->assertEquals($category->idnumber, reset($content[0]));
 230          } else {
 231              $this->assertEmpty($content);
 232          }
 233      }
 234  
 235      /**
 236       * Stress test datasource
 237       *
 238       * In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
 239       */
 240      public function test_stress_datasource(): void {
 241          if (!PHPUNIT_LONGTEST) {
 242              $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
 243          }
 244  
 245          $this->resetAfterTest();
 246  
 247          $category = $this->getDataGenerator()->create_category(['name' => 'My category']);
 248  
 249          $this->datasource_stress_test_columns(categories::class);
 250          $this->datasource_stress_test_columns_aggregation(categories::class);
 251          $this->datasource_stress_test_conditions(categories::class, 'course_category:idnumber');
 252      }
 253  }