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