Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 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  /**
  18   * Unit tests for /lib/gradelib.php.
  19   *
  20   * @package   core_grades
  21   * @category  phpunit
  22   * @copyright 2012 Andrew Davis
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  require_once($CFG->libdir . '/gradelib.php');
  30  
  31  class core_gradelib_testcase extends advanced_testcase {
  32  
  33      public function test_grade_update_mod_grades() {
  34  
  35          $this->resetAfterTest(true);
  36  
  37          // Create a broken module instance.
  38          $modinstance = new stdClass();
  39          $modinstance->modname = 'doesntexist';
  40  
  41          $this->assertFalse(grade_update_mod_grades($modinstance));
  42          // A debug message should have been generated.
  43          $this->assertDebuggingCalled();
  44  
  45          // Create a course and instance of mod_assign.
  46          $course = $this->getDataGenerator()->create_course();
  47  
  48          $assigndata['course'] = $course->id;
  49          $assigndata['name'] = 'lightwork assignment';
  50          $modinstance = self::getDataGenerator()->create_module('assign', $assigndata);
  51  
  52          // Function grade_update_mod_grades() requires 2 additional properties, cmidnumber and modname.
  53          $cm = get_coursemodule_from_instance('assign', $modinstance->id, 0, false, MUST_EXIST);
  54          $modinstance->cmidnumber = $cm->id;
  55          $modinstance->modname = 'assign';
  56  
  57          $this->assertTrue(grade_update_mod_grades($modinstance));
  58      }
  59  
  60      /**
  61       * Tests the function remove_grade_letters().
  62       */
  63      public function test_remove_grade_letters() {
  64          global $DB;
  65  
  66          $this->resetAfterTest();
  67  
  68          $course = $this->getDataGenerator()->create_course();
  69  
  70          $context = context_course::instance($course->id);
  71  
  72          // Add a grade letter to the course.
  73          $letter = new stdClass();
  74          $letter->letter = 'M';
  75          $letter->lowerboundary = '100';
  76          $letter->contextid = $context->id;
  77          $DB->insert_record('grade_letters', $letter);
  78  
  79          // Pre-warm the cache, ensure that that the letter is cached.
  80          $cache = cache::make('core', 'grade_letters');
  81  
  82          // Check that the cache is empty beforehand.
  83          $letters = $cache->get($context->id);
  84          $this->assertFalse($letters);
  85  
  86          // Call the function.
  87          grade_get_letters($context);
  88  
  89          $letters = $cache->get($context->id);
  90          $this->assertEquals(1, count($letters));
  91          $this->assertTrue(in_array($letter->letter, $letters));
  92  
  93          remove_grade_letters($context, false);
  94  
  95          // Confirm grade letter was deleted.
  96          $this->assertEquals(0, $DB->count_records('grade_letters'));
  97  
  98          // Confirm grade letter is also deleted from cache.
  99          $letters = $cache->get($context->id);
 100          $this->assertFalse($letters);
 101      }
 102  
 103      /**
 104       * Tests the function grade_course_category_delete().
 105       */
 106      public function test_grade_course_category_delete() {
 107          global $DB;
 108  
 109          $this->resetAfterTest();
 110  
 111          $category = core_course_category::create(array('name' => 'Cat1'));
 112  
 113          // Add a grade letter to the category.
 114          $letter = new stdClass();
 115          $letter->letter = 'M';
 116          $letter->lowerboundary = '100';
 117          $letter->contextid = context_coursecat::instance($category->id)->id;
 118          $DB->insert_record('grade_letters', $letter);
 119  
 120          grade_course_category_delete($category->id, '', false);
 121  
 122          // Confirm grade letter was deleted.
 123          $this->assertEquals(0, $DB->count_records('grade_letters'));
 124      }
 125  
 126      /**
 127       * Tests the function grade_regrade_final_grades().
 128       */
 129      public function test_grade_regrade_final_grades() {
 130          global $DB;
 131  
 132          $this->resetAfterTest();
 133  
 134          // Setup some basics.
 135          $course = $this->getDataGenerator()->create_course();
 136          $user = $this->getDataGenerator()->create_user();
 137          $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
 138  
 139          // We need two grade items.
 140          $params = ['idnumber' => 'g1', 'courseid' => $course->id];
 141          $g1 = new grade_item($this->getDataGenerator()->create_grade_item($params));
 142          unset($params['idnumber']);
 143          $g2 = new grade_item($this->getDataGenerator()->create_grade_item($params));
 144  
 145          $category = new grade_category($this->getDataGenerator()->create_grade_category($params));
 146          $catitem = $category->get_grade_item();
 147  
 148          // Now set a calculation.
 149          $catitem->set_calculation('=[[g1]]');
 150  
 151          $catitem->update();
 152  
 153          // Everything needs updating.
 154          $this->assertEquals(4, $DB->count_records('grade_items', ['courseid' => $course->id, 'needsupdate' => 1]));
 155  
 156          grade_regrade_final_grades($course->id);
 157  
 158          // See that everything has been updated.
 159          $this->assertEquals(0, $DB->count_records('grade_items', ['courseid' => $course->id, 'needsupdate' => 1]));
 160  
 161          $g1->delete();
 162  
 163          // Now there is one that needs updating.
 164          $this->assertEquals(1, $DB->count_records('grade_items', ['courseid' => $course->id, 'needsupdate' => 1]));
 165  
 166          // This can cause an infinite loop if things go... poorly.
 167          grade_regrade_final_grades($course->id);
 168  
 169          // Now because of the failure, two things need updating.
 170          $this->assertEquals(2, $DB->count_records('grade_items', ['courseid' => $course->id, 'needsupdate' => 1]));
 171      }
 172  
 173      /**
 174       * Tests for the grade_get_date_for_user_grade function.
 175       *
 176       * @dataProvider grade_get_date_for_user_grade_provider
 177       * @param stdClass $grade
 178       * @param stdClass $user
 179       * @param int $expected
 180       */
 181      public function test_grade_get_date_for_user_grade(stdClass $grade, stdClass $user, ?int $expected): void {
 182          $this->assertEquals($expected, grade_get_date_for_user_grade($grade, $user));
 183      }
 184  
 185      /**
 186       * Data provider for tests of the grade_get_date_for_user_grade function.
 187       *
 188       * @return array
 189       */
 190      public function grade_get_date_for_user_grade_provider(): array {
 191          $u1 = (object) [
 192              'id' => 42,
 193          ];
 194          $u2 = (object) [
 195              'id' => 930,
 196          ];
 197  
 198          $d1 = 1234567890;
 199          $d2 = 9876543210;
 200  
 201          $g1 = (object) [
 202              'usermodified' => $u1->id,
 203              'dategraded' => $d1,
 204              'datesubmitted' => $d2,
 205          ];
 206          $g2 = (object) [
 207              'usermodified' => $u1->id,
 208              'dategraded' => $d1,
 209              'datesubmitted' => 0,
 210          ];
 211  
 212          $g3 = (object) [
 213              'usermodified' => $u1->id,
 214              'dategraded' => null,
 215              'datesubmitted' => $d2,
 216          ];
 217  
 218          return [
 219              'If the user is the last person to have modified the grade_item then show the date that it was graded' => [
 220                  $g1,
 221                  $u1,
 222                  $d1,
 223              ],
 224              'If there is no grade and there is no feedback, then show graded date as null' => [
 225                  $g3,
 226                  $u1,
 227                  null,
 228              ],
 229              'If the user is not the last person to have modified the grade_item, ' .
 230              'and there is no submission date, then show the date that it was submitted' => [
 231                  $g1,
 232                  $u2,
 233                  $d2,
 234              ],
 235              'If the user is not the last person to have modified the grade_item, ' .
 236              'but there is no submission date, then show the date that it was graded' => [
 237                  $g2,
 238                  $u2,
 239                  $d1,
 240              ],
 241              'If the user is the last person to have modified the grade_item, ' .
 242              'and there is no submission date, then still show the date that it was graded' => [
 243                  $g2,
 244                  $u1,
 245                  $d1,
 246              ],
 247          ];
 248      }
 249  
 250      /**
 251       * Test the caching of grade letters.
 252       */
 253      public function test_get_grade_letters() {
 254  
 255          $this->resetAfterTest();
 256  
 257          // Setup some basics.
 258          $course = $this->getDataGenerator()->create_course();
 259          $context = context_course::instance($course->id);
 260  
 261          $cache = cache::make('core', 'grade_letters');
 262          $letters = $cache->get($context->id);
 263  
 264          // Make sure the cache is empty.
 265          $this->assertFalse($letters);
 266  
 267          // Now check to see if the letters get cached.
 268          $actual = grade_get_letters($context);
 269  
 270          $expected = $cache->get($context->id);
 271  
 272          $this->assertEquals($expected, $actual);
 273      }
 274  
 275      /**
 276       * Test custom letters.
 277       */
 278      public function test_get_grade_letters_custom() {
 279          global $DB;
 280  
 281          $this->resetAfterTest();
 282  
 283          $course = $this->getDataGenerator()->create_course();
 284          $context = context_course::instance($course->id);
 285  
 286          $cache = cache::make('core', 'grade_letters');
 287          $letters = $cache->get($context->id);
 288  
 289          // Make sure the cache is empty.
 290          $this->assertFalse($letters);
 291  
 292          // Add a grade letter to the course.
 293          $letter = new stdClass();
 294          $letter->letter = 'M';
 295          $letter->lowerboundary = '100';
 296          $letter->contextid = $context->id;
 297          $DB->insert_record('grade_letters', $letter);
 298  
 299          $actual = grade_get_letters($context);
 300          $expected = $cache->get($context->id);
 301  
 302          $this->assertEquals($expected, $actual);
 303      }
 304  }