Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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  namespace core_backup;
  18  
  19  use backup;
  20  use backup_controller;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
  26  require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
  27  require_once($CFG->libdir . '/completionlib.php');
  28  
  29  /**
  30   * Asyncronhous backup tests.
  31   *
  32   * @package    core_backup
  33   * @copyright  2018 Matt Porritt <mattp@catalyst-au.net>
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class async_backup_test extends \advanced_testcase {
  37  
  38      /**
  39       * Tests the asynchronous backup.
  40       */
  41      public function test_async_backup() {
  42          global $CFG, $DB, $USER;
  43  
  44          $this->resetAfterTest(true);
  45          $this->setAdminUser();
  46          $CFG->enableavailability = true;
  47          $CFG->enablecompletion = true;
  48  
  49          // Create a course with some availability data set.
  50          $generator = $this->getDataGenerator();
  51          $course = $generator->create_course(
  52                  array('format' => 'topics', 'numsections' => 3,
  53                          'enablecompletion' => COMPLETION_ENABLED),
  54                  array('createsections' => true));
  55          $forum = $generator->create_module('forum', array(
  56                  'course' => $course->id));
  57          $forum2 = $generator->create_module('forum', array(
  58                  'course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL));
  59  
  60          // Create a teacher user to call the backup.
  61          $teacher = $generator->create_user();
  62          $generator->enrol_user($teacher->id, $course->id, 'editingteacher');
  63  
  64          // We need a grade, easiest is to add an assignment.
  65          $assignrow = $generator->create_module('assign', array(
  66                  'course' => $course->id));
  67          $assign = new \assign(\context_module::instance($assignrow->cmid), false, false);
  68          $item = $assign->get_grade_item();
  69  
  70          // Make a test grouping as well.
  71          $grouping = $generator->create_grouping(array('courseid' => $course->id,
  72                  'name' => 'Grouping!'));
  73  
  74          $availability = '{"op":"|","show":false,"c":[' .
  75                  '{"type":"completion","cm":' . $forum2->cmid .',"e":1},' .
  76                  '{"type":"grade","id":' . $item->id . ',"min":4,"max":94},' .
  77                  '{"type":"grouping","id":' . $grouping->id . '}' .
  78                  ']}';
  79          $DB->set_field('course_modules', 'availability', $availability, array(
  80                  'id' => $forum->cmid));
  81          $DB->set_field('course_sections', 'availability', $availability, array(
  82                  'course' => $course->id, 'section' => 1));
  83  
  84          // Enable logging.
  85          $this->preventResetByRollback();
  86          set_config('enabled_stores', 'logstore_standard', 'tool_log');
  87          set_config('buffersize', 0, 'logstore_standard');
  88          get_log_manager(true);
  89  
  90          // Start backup process.
  91          $this->setUser($teacher->id);
  92  
  93          // Make the backup controller for an async backup.
  94          $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
  95                  backup::INTERACTIVE_YES, backup::MODE_ASYNC, $USER->id);
  96          $bc->finish_ui();
  97          $backupid = $bc->get_backupid();
  98          $bc->destroy();
  99  
 100          $prebackuprec = $DB->get_record('backup_controllers', array('backupid' => $backupid));
 101  
 102          // Check the initial backup controller was created correctly.
 103          $this->assertEquals(backup::STATUS_AWAITING, $prebackuprec->status);
 104          $this->assertEquals(2, $prebackuprec->execution);
 105  
 106          // Create the adhoc task.
 107          $asynctask = new \core\task\asynchronous_backup_task();
 108          $asynctask->set_blocking(false);
 109          $asynctask->set_custom_data(['backupid' => $backupid]);
 110          $asynctask->set_userid($USER->id);
 111          \core\task\manager::queue_adhoc_task($asynctask);
 112  
 113          // We are expecting trace output during this test.
 114          $this->expectOutputRegex("/$backupid/");
 115  
 116          // Execute adhoc task.
 117          $now = time();
 118          $task = \core\task\manager::get_next_adhoc_task($now);
 119          $this->assertInstanceOf('\\core\\task\\asynchronous_backup_task', $task);
 120          $task->execute();
 121          \core\task\manager::adhoc_task_complete($task);
 122  
 123          $postbackuprec = $DB->get_record('backup_controllers', ['backupid' => $backupid]);
 124  
 125          // Check backup was created successfully.
 126          $this->assertEquals(backup::STATUS_FINISHED_OK, $postbackuprec->status);
 127          $this->assertEquals(1.0, $postbackuprec->progress);
 128          $this->assertEquals($teacher->id, $postbackuprec->userid);
 129  
 130          // Check that the backupid was logged correctly.
 131          $logrec = $DB->get_record('logstore_standard_log', ['userid' => $postbackuprec->userid,
 132                  'target' => 'course_backup'], '*', MUST_EXIST);
 133          $otherdata = json_decode($logrec->other);
 134          $this->assertEquals($backupid, $otherdata->backupid);
 135      }
 136  
 137      /**
 138       * Tests the asynchronous backup will resolve in duplicate cases.
 139       */
 140      public function test_complete_async_backup() {
 141          global $CFG, $DB, $USER;
 142  
 143          $this->resetAfterTest(true);
 144          $this->setAdminUser();
 145          $CFG->enableavailability = true;
 146          $CFG->enablecompletion = true;
 147  
 148          // Create a course with some availability data set.
 149          $generator = $this->getDataGenerator();
 150          $course = $generator->create_course(
 151                  array('format' => 'topics', 'numsections' => 3,
 152                          'enablecompletion' => COMPLETION_ENABLED),
 153                  array('createsections' => true));
 154          $forum = $generator->create_module('forum', array(
 155                  'course' => $course->id));
 156          $forum2 = $generator->create_module('forum', array(
 157                  'course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL));
 158  
 159          // We need a grade, easiest is to add an assignment.
 160          $assignrow = $generator->create_module('assign', array(
 161                  'course' => $course->id));
 162          $assign = new \assign(\context_module::instance($assignrow->cmid), false, false);
 163          $item = $assign->get_grade_item();
 164  
 165          // Make a test grouping as well.
 166          $grouping = $generator->create_grouping(array('courseid' => $course->id,
 167                  'name' => 'Grouping!'));
 168  
 169          $availability = '{"op":"|","show":false,"c":[' .
 170                  '{"type":"completion","cm":' . $forum2->cmid .',"e":1},' .
 171                  '{"type":"grade","id":' . $item->id . ',"min":4,"max":94},' .
 172                  '{"type":"grouping","id":' . $grouping->id . '}' .
 173                  ']}';
 174          $DB->set_field('course_modules', 'availability', $availability, array(
 175                  'id' => $forum->cmid));
 176          $DB->set_field('course_sections', 'availability', $availability, array(
 177                  'course' => $course->id, 'section' => 1));
 178  
 179          // Make the backup controller for an async backup.
 180          $bc = new backup_controller(backup::TYPE_1COURSE, $course->id, backup::FORMAT_MOODLE,
 181                  backup::INTERACTIVE_YES, backup::MODE_ASYNC, $USER->id);
 182          $bc->finish_ui();
 183          $backupid = $bc->get_backupid();
 184          $bc->destroy();
 185  
 186          // Now hack the record to remove the controller and set the status fields to complete.
 187          // This emulates a duplicate run for an already finished controller.
 188          $id = $DB->get_field('backup_controllers', 'id', ['backupid' => $backupid]);
 189          $data = [
 190              'id' => $id,
 191              'controller' => '',
 192              'progress' => 1.0,
 193              'status' => backup::STATUS_FINISHED_OK
 194          ];
 195          $DB->update_record('backup_controllers', $data);
 196  
 197          // Now queue an adhoc task and check it handles and completes gracefully.
 198          $asynctask = new \core\task\asynchronous_backup_task();
 199          $asynctask->set_blocking(false);
 200          $asynctask->set_custom_data(array('backupid' => $backupid));
 201          \core\task\manager::queue_adhoc_task($asynctask);
 202  
 203          // We are expecting a specific message output during this test.
 204          $this->expectOutputRegex('/invalid controller/');
 205  
 206          // Execute adhoc task.
 207          $now = time();
 208          $task = \core\task\manager::get_next_adhoc_task($now);
 209          $this->assertInstanceOf('\\core\\task\\asynchronous_backup_task', $task);
 210          $task->execute();
 211          \core\task\manager::adhoc_task_complete($task);
 212  
 213          // Check the task record is removed.
 214          $this->assertEquals(0, $DB->count_records('task_adhoc'));
 215      }
 216  }