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 311 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  namespace core_completion;
  18  
  19  use completion_completion;
  20  
  21  /**
  22   * Test completion progress API.
  23   *
  24   * @package core_completion
  25   * @category test
  26   * @copyright 2017 Mark Nelson <markn@moodle.com>
  27   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   */
  29  class progress_test extends \advanced_testcase {
  30  
  31      /**
  32       * Test setup.
  33       */
  34      public function setUp(): void {
  35          global $CFG;
  36  
  37          $CFG->enablecompletion = true;
  38          $this->resetAfterTest();
  39      }
  40  
  41      /**
  42       * Tests that the course progress percentage is returned correctly when we have only activity completion.
  43       */
  44      public function test_course_progress_percentage_with_just_activities() {
  45          global $DB;
  46  
  47          // Add a course that supports completion.
  48          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
  49  
  50          // Enrol a user in the course.
  51          $user = $this->getDataGenerator()->create_user();
  52          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
  53          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
  54  
  55          // Add four activities that use completion.
  56          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id),
  57              array('completion' => 1));
  58          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id),
  59              array('completion' => 1));
  60          $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
  61              array('completion' => 1));
  62          $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
  63              array('completion' => 1));
  64  
  65          // Add an activity that does *not* use completion.
  66          $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
  67  
  68          // Mark two of them as completed for a user.
  69          $cmassign = get_coursemodule_from_id('assign', $assign->cmid);
  70          $cmdata = get_coursemodule_from_id('data', $data->cmid);
  71          $completion = new \completion_info($course);
  72          $completion->update_state($cmassign, COMPLETION_COMPLETE, $user->id);
  73          $completion->update_state($cmdata, COMPLETION_COMPLETE, $user->id);
  74  
  75          // Check we have received valid data.
  76          // Note - only 4 out of the 5 activities support completion, and the user has completed 2 of those.
  77          $this->assertEquals('50', \core_completion\progress::get_course_progress_percentage($course, $user->id));
  78      }
  79  
  80      /**
  81       * Tests that the course progress percentage is returned correctly when we have a course and activity completion.
  82       */
  83      public function test_course_progress_percentage_with_activities_and_course() {
  84          global $DB;
  85  
  86          // Add a course that supports completion.
  87          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
  88  
  89          // Enrol a user in the course.
  90          $user = $this->getDataGenerator()->create_user();
  91          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
  92          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
  93  
  94          // Add four activities that use completion.
  95          $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id),
  96              array('completion' => 1));
  97          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id),
  98              array('completion' => 1));
  99          $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
 100              array('completion' => 1));
 101          $this->getDataGenerator()->create_module('forum', array('course' => $course->id),
 102              array('completion' => 1));
 103  
 104          // Add an activity that does *not* use completion.
 105          $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
 106  
 107          // Mark two of them as completed for a user.
 108          $cmassign = get_coursemodule_from_id('assign', $assign->cmid);
 109          $cmdata = get_coursemodule_from_id('data', $data->cmid);
 110          $completion = new \completion_info($course);
 111          $completion->update_state($cmassign, COMPLETION_COMPLETE, $user->id);
 112          $completion->update_state($cmdata, COMPLETION_COMPLETE, $user->id);
 113  
 114          // Now, mark the course as completed.
 115          $ccompletion = new completion_completion(array('course' => $course->id, 'userid' => $user->id));
 116          $ccompletion->mark_complete();
 117  
 118          // Check we have received valid data.
 119          // The course completion takes priority, so should return 100.
 120          $this->assertEquals('100', \core_completion\progress::get_course_progress_percentage($course, $user->id));
 121      }
 122  
 123      /**
 124       * Tests that the course progress percentage is returned correctly for various grade to pass settings
 125       *
 126       * @covers \core_completion\progress::get_course_progress_percentage.
 127       */
 128      public function test_course_progress_percentage_completion_state() {
 129          global $DB, $CFG;
 130  
 131          require_once("{$CFG->dirroot}/completion/criteria/completion_criteria_activity.php");
 132  
 133          // Add a course that supports completion.
 134          $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
 135  
 136          // Enrol a user in the course.
 137          $teacher = $this->getDataGenerator()->create_user();
 138          $user = $this->getDataGenerator()->create_user();
 139          $studentrole = $DB->get_record('role', ['shortname' => 'student']);
 140          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 141          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
 142          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
 143  
 144          // Add three activities that use completion.
 145          /** @var \mod_assign_generator $assigngenerator */
 146          $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
 147          $assign['passgragepassed'] = $assigngenerator->create_instance([
 148              'course' => $course->id,
 149              'completion' => COMPLETION_ENABLED,
 150              'completionusegrade' => 1,
 151              'gradepass' => 50,
 152              'completionpassgrade' => 1
 153          ]);
 154  
 155          $assign['passgragefailed'] = $assigngenerator->create_instance([
 156              'course' => $course->id,
 157              'completion' => COMPLETION_ENABLED,
 158              'completionusegrade' => 1,
 159              'gradepass' => 50,
 160              'completionpassgrade' => 1
 161          ]);
 162  
 163          $assign['passgragenotused'] = $assigngenerator->create_instance([
 164              'course' => $course->id,
 165              'completion' => COMPLETION_ENABLED,
 166              'completionusegrade' => 1,
 167              'gradepass' => 50,
 168          ]);
 169  
 170          $assign['nograde'] = $assigngenerator->create_instance([
 171              'course' => $course->id,
 172              'completion' => COMPLETION_ENABLED,
 173          ]);
 174  
 175          $c = new \completion_info($course);
 176  
 177          foreach ($assign as $item) {
 178              $cmassing = get_coursemodule_from_id('assign', $item->cmid);
 179  
 180              // Add activity completion criteria.
 181              $criteriadata = new \stdClass();
 182              $criteriadata->id = $course->id;
 183              $criteriadata->criteria_activity = [];
 184              // Some activities.
 185              $criteriadata->criteria_activity[$cmassing->id] = 1;
 186              $criterion = new \completion_criteria_activity();
 187              $criterion->update_config($criteriadata);
 188          }
 189  
 190          $this->setUser($teacher);
 191  
 192          foreach ($assign as $key => $item) {
 193              $cm = get_coursemodule_from_instance('assign', $item->id);
 194  
 195              // Mark user completions.
 196              $completion = new \stdClass();
 197              $completion->coursemoduleid = $cm->id;
 198              $completion->timemodified = time();
 199              $completion->viewed = COMPLETION_NOT_VIEWED;
 200              $completion->overrideby = null;
 201  
 202              if ($key == 'passgragepassed') {
 203                  $completion->id = 0;
 204                  $completion->completionstate = COMPLETION_COMPLETE_PASS;
 205                  $completion->userid = $user->id;
 206                  $c->internal_set_data($cm, $completion, true);
 207              } else if ($key == 'passgragefailed') {
 208                  $completion->id = 0;
 209                  $completion->completionstate = COMPLETION_COMPLETE_FAIL;
 210                  $completion->userid = $user->id;
 211                  $c->internal_set_data($cm, $completion, true);
 212              } else if ($key == 'passgragenotused') {
 213                  $completion->id = 0;
 214                  $completion->completionstate = COMPLETION_COMPLETE;
 215                  $completion->userid = $user->id;
 216                  $c->internal_set_data($cm, $completion, true);
 217              } else if ($key == 'nograde') {
 218                  $completion->id = 0;
 219                  $completion->completionstate = COMPLETION_COMPLETE;
 220                  $completion->userid = $user->id;
 221                  $c->internal_set_data($cm, $completion, true);
 222              }
 223          }
 224  
 225          // Run course completions cron.
 226          \core_completion\api::mark_course_completions_activity_criteria();
 227  
 228          // Check we have received valid data.
 229          // Only assign2 is not completed.
 230          $this->assertEquals('75', \core_completion\progress::get_course_progress_percentage($course, $user->id));
 231      }
 232  
 233      /**
 234       * Tests that the course progress returns null when the course does not support it.
 235       */
 236      public function test_course_progress_course_not_using_completion() {
 237          // Create a course that does not use completion.
 238          $course = $this->getDataGenerator()->create_course();
 239  
 240          // Check that the result was null.
 241          $this->assertNull(\core_completion\progress::get_course_progress_percentage($course));
 242      }
 243  
 244      /**
 245       * Tests that the course progress returns null when there are no activities that support it.
 246       */
 247      public function test_course_progress_no_activities_using_completion() {
 248          // Create a course that does support completion.
 249          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
 250  
 251          // Add an activity that does *not* support completion.
 252          $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
 253  
 254          // Check that the result was null.
 255          $this->assertNull(\core_completion\progress::get_course_progress_percentage($course));
 256      }
 257  
 258      /**
 259       * Tests that the course progress returns null for a not tracked for completion user in a course.
 260       */
 261      public function test_course_progress_not_tracked_user() {
 262          global $DB;
 263  
 264          // Add a course that supports completion.
 265          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
 266  
 267          // Enrol a user in the course.
 268          $user = $this->getDataGenerator()->create_user();
 269          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 270  
 271          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
 272  
 273          // Now, mark the course as completed.
 274          $ccompletion = new completion_completion(array('course' => $course->id, 'userid' => $user->id));
 275          $ccompletion->mark_complete();
 276  
 277          // The course completion should return 100.
 278          $this->assertEquals('100', \core_completion\progress::get_course_progress_percentage($course, $user->id));
 279  
 280          // Now make the user's role to be not tracked for completion.
 281          unassign_capability('moodle/course:isincompletionreports', $studentrole->id);
 282  
 283          // Check that the result is null now.
 284          $this->assertNull(\core_completion\progress::get_course_progress_percentage($course, $user->id));
 285      }
 286  }