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 401 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_admin\reportbuilder\datasource;
  20  
  21  use core\task\database_logger;
  22  use core_reportbuilder_generator;
  23  use core_reportbuilder_testcase;
  24  use core_reportbuilder\local\filters\{boolean_select, date, duration, number, select, text};
  25  use core_reportbuilder\task\send_schedules;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php");
  31  
  32  /**
  33   * Unit tests for task logs datasource
  34   *
  35   * @package     core_admin
  36   * @covers      \core_admin\reportbuilder\datasource\task_logs
  37   * @copyright   2022 Paul Holden <paulh@moodle.com>
  38   * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class task_logs_test extends core_reportbuilder_testcase {
  41  
  42      /**
  43       * Test default datasource
  44       */
  45      public function test_datasource_default(): void {
  46          $this->resetAfterTest();
  47  
  48          $this->generate_task_log_data(true, 3, 2, 1654038000, 1654038060);
  49  
  50          /** @var core_reportbuilder_generator $generator */
  51          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
  52          $report = $generator->create_report(['name' => 'Tasks', 'source' => task_logs::class, 'default' => 1]);
  53  
  54          $content = $this->get_custom_report_content($report->get('id'));
  55          $this->assertCount(1, $content);
  56  
  57          // Default columns are name, starttime, duration, result.
  58          [$name, $timestart, $duration, $result] = array_values($content[0]);
  59          $this->assertStringContainsString(send_schedules::class, $name);
  60          $this->assertEquals('1/06/22, 07:00:00', $timestart);
  61          $this->assertEquals('1 min', $duration);
  62          $this->assertEquals('Success', $result);
  63      }
  64  
  65      /**
  66       * Test datasource columns that aren't added by default
  67       */
  68      public function test_datasource_non_default_columns(): void {
  69          $this->resetAfterTest();
  70  
  71          $this->generate_task_log_data(true, 3, 2, 1654038000, 1654038060, 'hi', 'core_reportbuilder', 'test', 43);
  72  
  73          /** @var core_reportbuilder_generator $generator */
  74          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
  75          $report = $generator->create_report(['name' => 'Tasks', 'source' => task_logs::class, 'default' => 0]);
  76  
  77          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'task_log:component']);
  78          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'task_log:type']);
  79          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'task_log:endtime']);
  80          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'task_log:hostname']);
  81          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'task_log:pid']);
  82          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'task_log:database']);
  83          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'task_log:dbreads']);
  84          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'task_log:dbwrites']);
  85  
  86          $content = $this->get_custom_report_content($report->get('id'));
  87          $this->assertCount(1, $content);
  88  
  89          $this->assertEquals([
  90              'core_reportbuilder',
  91              'Scheduled',
  92              '1/06/22, 07:01:00',
  93              'test',
  94              '43',
  95              '<div>3 reads</div><div>2 writes</div>',
  96              '3',
  97              '2',
  98          ], array_values($content[0]));
  99      }
 100  
 101      /**
 102       * Data provider for {@see test_datasource_filters}
 103       *
 104       * @return array[]
 105       */
 106      public function datasource_filters_provider(): array {
 107          return [
 108              'Filter name' => ['task_log:name', [
 109                  'task_log:name_values' => [send_schedules::class],
 110              ], true],
 111              'Filter name (no match)' => ['task_log:name', [
 112                  'task_log:name_values' => ['invalid'],
 113              ], false],
 114              'Filter component' => ['task_log:component', [
 115                  'task_log:component_operator' => select::EQUAL_TO,
 116                  'task_log:component_value' => 'core_reportbuilder',
 117              ], true],
 118              'Filter component (no match)' => ['task_log:component', [
 119                  'task_log:component_operator' => select::NOT_EQUAL_TO,
 120                  'task_log:component_value' => 'core_reportbuilder',
 121              ], false],
 122              'Filter type' => ['task_log:type', [
 123                  'task_log:type_operator' => select::EQUAL_TO,
 124                  'task_log:type_value' => database_logger::TYPE_SCHEDULED,
 125              ], true],
 126              'Filter type (no match)' => ['task_log:type', [
 127                  'task_log:type_operator' => select::EQUAL_TO,
 128                  'task_log:type_value' => database_logger::TYPE_ADHOC,
 129              ], false],
 130              'Filter output' => ['task_log:output', [
 131                  'task_log:output_operator' => text::IS_NOT_EMPTY,
 132              ], true],
 133              'Filter output (no match)' => ['task_log:output', [
 134                  'task_log:output_operator' => text::IS_EMPTY,
 135              ], false],
 136              'Filter result' => ['task_log:result', [
 137                  'task_log:result_operator' => boolean_select::CHECKED,
 138              ], true],
 139              'Filter result (no match)' => ['task_log:result', [
 140                  'task_log:result_operator' => boolean_select::NOT_CHECKED,
 141              ], false],
 142              'Filter time start' => ['task_log:timestart', [
 143                  'task_log:timestart_operator' => date::DATE_RANGE,
 144                  'task_log:timestart_from' => 1622502000,
 145              ], true],
 146              'Filter time start (no match)' => ['task_log:timestart', [
 147                  'task_log:timestart_operator' => date::DATE_RANGE,
 148                  'task_log:timestart_to' => 1622502000,
 149              ], false],
 150              'Filter time end' => ['task_log:timeend', [
 151                  'task_log:timeend_operator' => date::DATE_RANGE,
 152                  'task_log:timeend_from' => 1622502000,
 153              ], true],
 154              'Filter time end (no match)' => ['task_log:timeend', [
 155                  'task_log:timeend_operator' => date::DATE_RANGE,
 156                  'task_log:timeend_to' => 1622502000,
 157              ], false],
 158              'Filter duration' => ['task_log:duration', [
 159                  'task_log:duration_operator' => duration::DURATION_MAXIMUM,
 160                  'task_log:duration_unit' => MINSECS,
 161                  'task_log:duration_value' => 2,
 162              ], true],
 163              'Filter duration (no match)' => ['task_log:duration', [
 164                  'task_log:duration_operator' => duration::DURATION_MINIMUM,
 165                  'task_log:duration_unit' => MINSECS,
 166                  'task_log:duration_value' => 2,
 167              ], false],
 168              'Filter database reads' => ['task_log:dbreads', [
 169                  'task_log:dbreads_operator' => number::LESS_THAN,
 170                  'task_log:dbreads_value1' => 4,
 171              ], true],
 172              'Filter database reads (no match)' => ['task_log:dbreads', [
 173                  'task_log:dbreads_operator' => number::GREATER_THAN,
 174                  'task_log:dbreads_value1' => 4,
 175              ], false],
 176              'Filter database writes' => ['task_log:dbwrites', [
 177                  'task_log:dbwrites_operator' => number::LESS_THAN,
 178                  'task_log:dbwrites_value1' => 4,
 179              ], true],
 180              'Filter database writes (no match)' => ['task_log:dbwrites', [
 181                  'task_log:dbwrites_operator' => number::GREATER_THAN,
 182                  'task_log:dbwrites_value1' => 4,
 183              ], false],
 184          ];
 185      }
 186  
 187      /**
 188       * Test datasource filters
 189       *
 190       * @param string $filtername
 191       * @param array $filtervalues
 192       * @param bool $expectmatch
 193       *
 194       * @dataProvider datasource_filters_provider
 195       */
 196      public function test_datasource_filters(
 197          string $filtername,
 198          array $filtervalues,
 199          bool $expectmatch
 200      ): void {
 201          $this->resetAfterTest();
 202  
 203          $this->generate_task_log_data(true, 3, 2, 1654038000, 1654038060, 'hi', 'core_reportbuilder', 'test', 43);
 204  
 205          /** @var core_reportbuilder_generator $generator */
 206          $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
 207  
 208          // Create report containing single component column, and given filter.
 209          $report = $generator->create_report(['name' => 'Tasks', 'source' => task_logs::class, 'default' => 0]);
 210          $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'task_log:component']);
 211  
 212          // Add filter, set it's values.
 213          $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]);
 214          $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues);
 215  
 216          if ($expectmatch) {
 217              $this->assertCount(1, $content);
 218              $this->assertEquals('core_reportbuilder', reset($content[0]));
 219          } else {
 220              $this->assertEmpty($content);
 221          }
 222      }
 223  
 224      /**
 225       * Stress test datasource
 226       *
 227       * In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php
 228       */
 229      public function test_stress_datasource(): void {
 230          if (!PHPUNIT_LONGTEST) {
 231              $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
 232          }
 233  
 234          $this->resetAfterTest();
 235  
 236          $this->generate_task_log_data(true, 3, 2, 1654038000, 1654038060, 'hi', 'core_reportbuilder', 'test', 43);
 237  
 238          $this->datasource_stress_test_columns(task_logs::class);
 239          $this->datasource_stress_test_columns_aggregation(task_logs::class);
 240          $this->datasource_stress_test_conditions(task_logs::class, 'task_log:name');
 241      }
 242  
 243      /**
 244       * Helper to generate some task logs data
 245       *
 246       * @param bool $success
 247       * @param int $dbreads
 248       * @param int $dbwrites
 249       * @param float $timestart
 250       * @param float $timeend
 251       * @param string $logoutput
 252       * @param string $component
 253       * @param string $hostname
 254       * @param int $pid
 255       */
 256      private function generate_task_log_data(
 257          bool $success,
 258          int $dbreads,
 259          int $dbwrites,
 260          float $timestart,
 261          float $timeend,
 262          string $logoutput = 'hello',
 263          string $component = 'moodle',
 264          string $hostname = 'phpunit',
 265          int $pid = 42
 266      ): void {
 267  
 268          $logpath = make_request_directory() . '/log.txt';
 269          file_put_contents($logpath, $logoutput);
 270  
 271          $task = new send_schedules();
 272          $task->set_component($component);
 273          $task->set_hostname($hostname);
 274          $task->set_pid($pid);
 275  
 276          database_logger::store_log_for_task($task, $logpath, !$success, $dbreads, $dbwrites, $timestart, $timeend);
 277      }
 278  }