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 310 and 401] [Versions 39 and 401]

   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  /**
  18   * Unit tests for backups cron helper.
  19   *
  20   * @package   core_backup
  21   * @category  test
  22   * @copyright 2012 Frédéric Massart <fred@moodle.com>
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace core_backup;
  27  
  28  use backup;
  29  use backup_cron_automated_helper;
  30  
  31  defined('MOODLE_INTERNAL') || die();
  32  
  33  global $CFG;
  34  require_once($CFG->dirroot . '/backup/util/helper/backup_cron_helper.class.php');
  35  require_once($CFG->dirroot . '/backup/util/interfaces/checksumable.class.php');
  36  require_once("$CFG->dirroot/backup/backup.class.php");
  37  
  38  /**
  39   * Unit tests for backups cron helper.
  40   *
  41   * @package   core_backup
  42   * @category  test
  43   * @copyright 2012 Frédéric Massart <fred@moodle.com>
  44   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  45   */
  46  class cronhelper_test extends \advanced_testcase {
  47      /**
  48       * Test {@link backup_cron_automated_helper::calculate_next_automated_backup}.
  49       */
  50      public function test_next_automated_backup() {
  51          global $CFG;
  52  
  53          $this->resetAfterTest();
  54          set_config('backup_auto_active', '1', 'backup');
  55  
  56          $this->setTimezone('Australia/Perth');
  57  
  58          // Notes
  59          // - backup_auto_weekdays starts on Sunday
  60          // - Tests cannot be done in the past
  61          // - Only the DST on the server side is handled.
  62  
  63          // Every Tue and Fri at 11pm.
  64          set_config('backup_auto_weekdays', '0010010', 'backup');
  65          set_config('backup_auto_hour', '23', 'backup');
  66          set_config('backup_auto_minute', '0', 'backup');
  67  
  68          $timezone = 99; // Ignored, everything is calculated in server timezone!!!
  69  
  70          $now = strtotime('next Monday 17:00:00');
  71          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
  72          $this->assertEquals('2-23:00', date('w-H:i', $next));
  73  
  74          $now = strtotime('next Tuesday 18:00:00');
  75          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
  76          $this->assertEquals('2-23:00', date('w-H:i', $next));
  77  
  78          $now = strtotime('next Wednesday 17:00:00');
  79          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
  80          $this->assertEquals('5-23:00', date('w-H:i', $next));
  81  
  82          $now = strtotime('next Thursday 17:00:00');
  83          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
  84          $this->assertEquals('5-23:00', date('w-H:i', $next));
  85  
  86          $now = strtotime('next Friday 17:00:00');
  87          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
  88          $this->assertEquals('5-23:00', date('w-H:i', $next));
  89  
  90          $now = strtotime('next Saturday 17:00:00');
  91          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
  92          $this->assertEquals('2-23:00', date('w-H:i', $next));
  93  
  94          $now = strtotime('next Sunday 17:00:00');
  95          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
  96          $this->assertEquals('2-23:00', date('w-H:i', $next));
  97  
  98          // Every Sun and Sat at 12pm.
  99          set_config('backup_auto_weekdays', '1000001', 'backup');
 100          set_config('backup_auto_hour', '0', 'backup');
 101          set_config('backup_auto_minute', '0', 'backup');
 102  
 103          $now = strtotime('next Monday 17:00:00');
 104          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 105          $this->assertEquals('6-00:00', date('w-H:i', $next));
 106  
 107          $now = strtotime('next Tuesday 17:00:00');
 108          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 109          $this->assertEquals('6-00:00', date('w-H:i', $next));
 110  
 111          $now = strtotime('next Wednesday 17:00:00');
 112          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 113          $this->assertEquals('6-00:00', date('w-H:i', $next));
 114  
 115          $now = strtotime('next Thursday 17:00:00');
 116          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 117          $this->assertEquals('6-00:00', date('w-H:i', $next));
 118  
 119          $now = strtotime('next Friday 17:00:00');
 120          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 121          $this->assertEquals('6-00:00', date('w-H:i', $next));
 122  
 123          $now = strtotime('next Saturday 17:00:00');
 124          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 125          $this->assertEquals('0-00:00', date('w-H:i', $next));
 126  
 127          $now = strtotime('next Sunday 17:00:00');
 128          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 129          $this->assertEquals('6-00:00', date('w-H:i', $next));
 130  
 131          // Every Sun at 4am.
 132          set_config('backup_auto_weekdays', '1000000', 'backup');
 133          set_config('backup_auto_hour', '4', 'backup');
 134          set_config('backup_auto_minute', '0', 'backup');
 135  
 136          $now = strtotime('next Monday 17:00:00');
 137          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 138          $this->assertEquals('0-04:00', date('w-H:i', $next));
 139  
 140          $now = strtotime('next Tuesday 17:00:00');
 141          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 142          $this->assertEquals('0-04:00', date('w-H:i', $next));
 143  
 144          $now = strtotime('next Wednesday 17:00:00');
 145          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 146          $this->assertEquals('0-04:00', date('w-H:i', $next));
 147  
 148          $now = strtotime('next Thursday 17:00:00');
 149          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 150          $this->assertEquals('0-04:00', date('w-H:i', $next));
 151  
 152          $now = strtotime('next Friday 17:00:00');
 153          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 154          $this->assertEquals('0-04:00', date('w-H:i', $next));
 155  
 156          $now = strtotime('next Saturday 17:00:00');
 157          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 158          $this->assertEquals('0-04:00', date('w-H:i', $next));
 159  
 160          $now = strtotime('next Sunday 17:00:00');
 161          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 162          $this->assertEquals('0-04:00', date('w-H:i', $next));
 163  
 164          // Every day but Wed at 8:30pm.
 165          set_config('backup_auto_weekdays', '1110111', 'backup');
 166          set_config('backup_auto_hour', '20', 'backup');
 167          set_config('backup_auto_minute', '30', 'backup');
 168  
 169          $now = strtotime('next Monday 17:00:00');
 170          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 171          $this->assertEquals('1-20:30', date('w-H:i', $next));
 172  
 173          $now = strtotime('next Tuesday 17:00:00');
 174          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 175          $this->assertEquals('2-20:30', date('w-H:i', $next));
 176  
 177          $now = strtotime('next Wednesday 17:00:00');
 178          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 179          $this->assertEquals('4-20:30', date('w-H:i', $next));
 180  
 181          $now = strtotime('next Thursday 17:00:00');
 182          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 183          $this->assertEquals('4-20:30', date('w-H:i', $next));
 184  
 185          $now = strtotime('next Friday 17:00:00');
 186          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 187          $this->assertEquals('5-20:30', date('w-H:i', $next));
 188  
 189          $now = strtotime('next Saturday 17:00:00');
 190          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 191          $this->assertEquals('6-20:30', date('w-H:i', $next));
 192  
 193          $now = strtotime('next Sunday 17:00:00');
 194          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 195          $this->assertEquals('0-20:30', date('w-H:i', $next));
 196  
 197          // Sun, Tue, Thu, Sat at 12pm.
 198          set_config('backup_auto_weekdays', '1010101', 'backup');
 199          set_config('backup_auto_hour', '0', 'backup');
 200          set_config('backup_auto_minute', '0', 'backup');
 201  
 202          $now = strtotime('next Monday 13:00:00');
 203          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 204          $this->assertEquals('2-00:00', date('w-H:i', $next));
 205  
 206          $now = strtotime('next Tuesday 13:00:00');
 207          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 208          $this->assertEquals('4-00:00', date('w-H:i', $next));
 209  
 210          $now = strtotime('next Wednesday 13:00:00');
 211          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 212          $this->assertEquals('4-00:00', date('w-H:i', $next));
 213  
 214          $now = strtotime('next Thursday 13:00:00');
 215          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 216          $this->assertEquals('6-00:00', date('w-H:i', $next));
 217  
 218          $now = strtotime('next Friday 13:00:00');
 219          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 220          $this->assertEquals('6-00:00', date('w-H:i', $next));
 221  
 222          $now = strtotime('next Saturday 13:00:00');
 223          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 224          $this->assertEquals('0-00:00', date('w-H:i', $next));
 225  
 226          $now = strtotime('next Sunday 13:00:00');
 227          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 228          $this->assertEquals('2-00:00', date('w-H:i', $next));
 229  
 230          // None.
 231          set_config('backup_auto_weekdays', '0000000', 'backup');
 232          set_config('backup_auto_hour', '15', 'backup');
 233          set_config('backup_auto_minute', '30', 'backup');
 234  
 235          $now = strtotime('next Sunday 13:00:00');
 236          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 237          $this->assertEquals('0', $next);
 238  
 239          // Playing with timezones.
 240          set_config('backup_auto_weekdays', '1111111', 'backup');
 241          set_config('backup_auto_hour', '20', 'backup');
 242          set_config('backup_auto_minute', '00', 'backup');
 243  
 244          $this->setTimezone('Australia/Perth');
 245          $now = strtotime('18:00:00');
 246          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 247          $this->assertEquals(date('w-20:00'), date('w-H:i', $next));
 248  
 249          $this->setTimezone('Europe/Brussels');
 250          $now = strtotime('18:00:00');
 251          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 252          $this->assertEquals(date('w-20:00'), date('w-H:i', $next));
 253  
 254          $this->setTimezone('America/New_York');
 255          $now = strtotime('18:00:00');
 256          $next = backup_cron_automated_helper::calculate_next_automated_backup($timezone, $now);
 257          $this->assertEquals(date('w-20:00'), date('w-H:i', $next));
 258      }
 259  
 260      /**
 261       * Test {@link backup_cron_automated_helper::get_backups_to_delete}.
 262       */
 263      public function test_get_backups_to_delete() {
 264          $this->resetAfterTest();
 265          // Active only backup_auto_max_kept config to 2 days.
 266          set_config('backup_auto_max_kept', '2', 'backup');
 267          set_config('backup_auto_delete_days', '0', 'backup');
 268          set_config('backup_auto_min_kept', '0', 'backup');
 269  
 270          // No backups to delete.
 271          $backupfiles = array(
 272              '1000000000' => 'file1.mbz',
 273              '1000432000' => 'file3.mbz'
 274          );
 275          $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1000432000);
 276          $this->assertFalse($deletedbackups);
 277  
 278          // Older backup to delete.
 279          $backupfiles['1000172800'] = 'file2.mbz';
 280          $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1000432000);
 281          $this->assertEquals(1, count($deletedbackups));
 282          $this->assertArrayHasKey('1000000000', $backupfiles);
 283          $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
 284  
 285          // Activate backup_auto_max_kept to 5 days and backup_auto_delete_days to 10 days.
 286          set_config('backup_auto_max_kept', '5', 'backup');
 287          set_config('backup_auto_delete_days', '10', 'backup');
 288          set_config('backup_auto_min_kept', '0', 'backup');
 289  
 290          // No backups to delete. Timestamp is 1000000000 + 10 days.
 291          $backupfiles['1000432001'] = 'file4.mbz';
 292          $backupfiles['1000864000'] = 'file5.mbz';
 293          $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1000864000);
 294          $this->assertFalse($deletedbackups);
 295  
 296          // One old backup to delete. Timestamp is 1000000000 + 10 days + 1 second.
 297          $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1000864001);
 298          $this->assertEquals(1, count($deletedbackups));
 299          $this->assertArrayHasKey('1000000000', $backupfiles);
 300          $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
 301  
 302          // Two old backups to delete. Timestamp is 1000000000 + 12 days + 1 second.
 303          $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1001036801);
 304          $this->assertEquals(2, count($deletedbackups));
 305          $this->assertArrayHasKey('1000000000', $backupfiles);
 306          $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
 307          $this->assertArrayHasKey('1000172800', $backupfiles);
 308          $this->assertEquals('file2.mbz', $backupfiles['1000172800']);
 309  
 310          // Activate backup_auto_max_kept to 5 days, backup_auto_delete_days to 10 days and backup_auto_min_kept to 2.
 311          set_config('backup_auto_max_kept', '5', 'backup');
 312          set_config('backup_auto_delete_days', '10', 'backup');
 313          set_config('backup_auto_min_kept', '2', 'backup');
 314  
 315          // Three instead of four old backups are deleted. Timestamp is 1000000000 + 16 days.
 316          $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1001382400);
 317          $this->assertEquals(3, count($deletedbackups));
 318          $this->assertArrayHasKey('1000000000', $backupfiles);
 319          $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
 320          $this->assertArrayHasKey('1000172800', $backupfiles);
 321          $this->assertEquals('file2.mbz', $backupfiles['1000172800']);
 322          $this->assertArrayHasKey('1000432000', $backupfiles);
 323          $this->assertEquals('file3.mbz', $backupfiles['1000432000']);
 324  
 325          // Three instead of all five backups are deleted. Timestamp is 1000000000 + 60 days.
 326          $deletedbackups = testable_backup_cron_automated_helper::testable_get_backups_to_delete($backupfiles, 1005184000);
 327          $this->assertEquals(3, count($deletedbackups));
 328          $this->assertArrayHasKey('1000000000', $backupfiles);
 329          $this->assertEquals('file1.mbz', $backupfiles['1000000000']);
 330          $this->assertArrayHasKey('1000172800', $backupfiles);
 331          $this->assertEquals('file2.mbz', $backupfiles['1000172800']);
 332          $this->assertArrayHasKey('1000432000', $backupfiles);
 333          $this->assertEquals('file3.mbz', $backupfiles['1000432000']);
 334      }
 335  
 336      /**
 337       * Test {@link backup_cron_automated_helper::is_course_modified}.
 338       */
 339      public function test_is_course_modified() {
 340          $this->resetAfterTest();
 341          $this->preventResetByRollback();
 342  
 343          set_config('enabled_stores', 'logstore_standard', 'tool_log');
 344          set_config('buffersize', 0, 'logstore_standard');
 345          set_config('logguests', 1, 'logstore_standard');
 346  
 347          $course = $this->getDataGenerator()->create_course();
 348  
 349          // New courses should be backed up.
 350          $this->assertTrue(testable_backup_cron_automated_helper::testable_is_course_modified($course->id, 0));
 351  
 352          $timepriortobackup = time();
 353          $this->waitForSecond();
 354          $otherarray = [
 355              'format' => backup::FORMAT_MOODLE,
 356              'mode' => backup::MODE_GENERAL,
 357              'interactive' => backup::INTERACTIVE_YES,
 358              'type' => backup::TYPE_1COURSE,
 359          ];
 360          $event = \core\event\course_backup_created::create([
 361              'objectid' => $course->id,
 362              'context'  => \context_course::instance($course->id),
 363              'other'    => $otherarray
 364          ]);
 365          $event->trigger();
 366  
 367          // If the only action since last backup was a backup then no backup.
 368          $this->assertFalse(testable_backup_cron_automated_helper::testable_is_course_modified($course->id, $timepriortobackup));
 369  
 370          $course->groupmode = SEPARATEGROUPS;
 371          $course->groupmodeforce = true;
 372          update_course($course);
 373  
 374          // Updated courses should be backed up.
 375          $this->assertTrue(testable_backup_cron_automated_helper::testable_is_course_modified($course->id, $timepriortobackup));
 376      }
 377  
 378      /**
 379       * Create courses and backup records for tests.
 380       *
 381       * @return array Created courses.
 382       */
 383      private function course_setup() {
 384          global $DB;
 385  
 386          // Create test courses.
 387          $course1 = $this->getDataGenerator()->create_course(array('timecreated' => 1553402000)); // Newest.
 388          $course2 = $this->getDataGenerator()->create_course(array('timecreated' => 1552179600));
 389          $course3 = $this->getDataGenerator()->create_course(array('timecreated' => 1552179600));
 390          $course4 = $this->getDataGenerator()->create_course(array('timecreated' => 1552179600));
 391  
 392          // Create backup course records for the courses that need them.
 393          $backupcourse3 = new \stdClass;
 394          $backupcourse3->courseid = $course3->id;
 395          $backupcourse3->laststatus = testable_backup_cron_automated_helper::BACKUP_STATUS_OK;
 396          $backupcourse3->nextstarttime = 1554822160;
 397          $DB->insert_record('backup_courses', $backupcourse3);
 398  
 399          $backupcourse4 = new \stdClass;
 400          $backupcourse4->courseid = $course4->id;
 401          $backupcourse4->laststatus = testable_backup_cron_automated_helper::BACKUP_STATUS_OK;
 402          $backupcourse4->nextstarttime = 1554858160;
 403          $DB->insert_record('backup_courses', $backupcourse4);
 404  
 405          return array($course1, $course2, $course3, $course4);
 406      }
 407  
 408      /**
 409       * Test the selection and ordering of courses to be backed up.
 410       */
 411      public function test_get_courses() {
 412          $this->resetAfterTest();
 413  
 414          list($course1, $course2, $course3, $course4) = $this->course_setup();
 415  
 416          $now = 1559215025;
 417  
 418          // Get the courses in order.
 419          $courseset = testable_backup_cron_automated_helper::testable_get_courses($now);
 420  
 421          $coursearray = array();
 422          foreach ($courseset as $course) {
 423              if ($course->id != SITEID) { // Skip system course for test.
 424                  $coursearray[] = $course->id;
 425              }
 426  
 427          }
 428          $courseset->close();
 429  
 430          // First should be course 1, it is the more recently modified without a backup.
 431          $this->assertEquals($course1->id, $coursearray[0]);
 432  
 433          // Second should be course 2, it is the next more recently modified without a backup.
 434          $this->assertEquals($course2->id, $coursearray[1]);
 435  
 436          // Third should be course 3, it is the course with the oldest backup.
 437          $this->assertEquals($course3->id, $coursearray[2]);
 438  
 439          // Fourth should be course 4, it is the course with the newest backup.
 440          $this->assertEquals($course4->id, $coursearray[3]);
 441      }
 442  
 443      /**
 444       * Test the selection and ordering of courses to be backed up.
 445       * Where it is not yet time to start backups for courses with existing backups.
 446       */
 447      public function test_get_courses_starttime() {
 448          $this->resetAfterTest();
 449  
 450          list($course1, $course2, $course3, $course4) = $this->course_setup();
 451  
 452          $now = 1554858000;
 453  
 454          // Get the courses in order.
 455          $courseset = testable_backup_cron_automated_helper::testable_get_courses($now);
 456  
 457          $coursearray = array();
 458          foreach ($courseset as $course) {
 459              if ($course->id != SITEID) { // Skip system course for test.
 460                  $coursearray[] = $course->id;
 461              }
 462  
 463          }
 464          $courseset->close();
 465  
 466          // Should only be two courses.
 467          // First should be course 1, it is the more recently modified without a backup.
 468          $this->assertEquals($course1->id, $coursearray[0]);
 469  
 470          // Second should be course 2, it is the next more recently modified without a backup.
 471          $this->assertEquals($course2->id, $coursearray[1]);
 472      }
 473  
 474  }
 475  
 476  
 477  
 478  /**
 479   * Provides access to protected methods we want to explicitly test
 480   *
 481   * @copyright 2015 Jean-Philippe Gaudreau <jp.gaudreau@umontreal.ca>
 482   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 483   */
 484  class testable_backup_cron_automated_helper extends backup_cron_automated_helper {
 485  
 486      /**
 487       * Provides access to protected method get_backups_to_remove.
 488       *
 489       * @param array $backupfiles Existing backup files
 490       * @param int $now Starting time of the process
 491       * @return array Backup files to remove
 492       */
 493      public static function testable_get_backups_to_delete($backupfiles, $now) {
 494          return parent::get_backups_to_delete($backupfiles, $now);
 495      }
 496  
 497      /**
 498       * Provides access to protected method get_backups_to_remove.
 499       *
 500       * @param int $courseid course id to check
 501       * @param int $since timestamp, from which to check
 502       *
 503       * @return bool true if the course was modified, false otherwise. This also returns false if no readers are enabled. This is
 504       * intentional, since we cannot reliably determine if any modification was made or not.
 505       */
 506      public static function testable_is_course_modified($courseid, $since) {
 507          return parent::is_course_modified($courseid, $since);
 508      }
 509  
 510      /**
 511       * Provides access to protected method get_courses.
 512       *
 513       * @param int $now Timestamp to use.
 514       * @return moodle_recordset The returned courses as a Moodle recordest.
 515       */
 516      public static function testable_get_courses($now) {
 517          return parent::get_courses($now);
 518      }
 519  
 520  }