See Release Notes
Long Term Support Release
Differences Between: [Versions 311 and 401] [Versions 401 and 402] [Versions 401 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; 18 19 use core_grades_external; 20 21 defined('MOODLE_INTERNAL') || die(); 22 23 global $CFG; 24 25 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 26 27 /** 28 * Grades functions unit tests 29 * 30 * Unit tests for the grade API at /lib/classes/grades_external.php 31 * 32 * @package core 33 * @category test 34 * @copyright 2012 Andrew Davis 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 class grades_external_test extends \externallib_advanced_testcase { 38 39 /** 40 * Load initial test information 41 * 42 * @param string $assignmentname Assignment name 43 * @param int $student1rawgrade Student 1 grade 44 * @param int $student2rawgrade Student 2 grade 45 * @return array Array of vars with test information 46 */ 47 protected function load_test_data($assignmentname, $student1rawgrade, $student2rawgrade) { 48 global $DB; 49 50 // Adds a course, a teacher, 2 students, an assignment and grades for the students. 51 $course = $this->getDataGenerator()->create_course(); 52 $coursecontext = \context_course::instance($course->id); 53 54 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 55 56 $student1 = $this->getDataGenerator()->create_user(); 57 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id); 58 59 $student2 = $this->getDataGenerator()->create_user(); 60 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id); 61 62 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 63 $teacher = $this->getDataGenerator()->create_user(); 64 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id); 65 66 $parent = $this->getDataGenerator()->create_user(); 67 $this->setUser($parent); 68 $student1context = \context_user::instance($student1->id); 69 // Creates a new role, gives it the capability and gives $USER that role. 70 $parentroleid = $this->assignUserCapability('moodle/grade:viewall', $student1context->id); 71 // Enrol the user in the course using the new role. 72 $this->getDataGenerator()->enrol_user($parent->id, $course->id, $parentroleid); 73 74 $assignment = $this->getDataGenerator()->create_module('assign', array('name' => $assignmentname, 'course' => $course->id)); 75 $modcontext = get_coursemodule_from_instance('assign', $assignment->id, $course->id); 76 $assignment->cmidnumber = $modcontext->id; 77 78 $student1grade = array('userid' => $student1->id, 'rawgrade' => $student1rawgrade); 79 $student2grade = array('userid' => $student2->id, 'rawgrade' => $student2rawgrade); 80 $studentgrades = array($student1->id => $student1grade, $student2->id => $student2grade); 81 assign_grade_item_update($assignment, $studentgrades); 82 83 // Insert a custom grade scale to be used by an outcome. 84 $gradescale = new \grade_scale(); 85 $gradescale->name = 'unittestscale3'; 86 $gradescale->courseid = $course->id; 87 $gradescale->userid = 0; 88 $gradescale->scale = 'Distinction, Very Good, Good, Pass, Fail'; 89 $gradescale->description = 'This scale is used to mark standard assignments.'; 90 $gradescale->insert(); 91 92 // Insert an outcome. 93 $data = new \stdClass(); 94 $data->courseid = $course->id; 95 $data->fullname = 'Team work'; 96 $data->shortname = 'Team work'; 97 $data->scaleid = $gradescale->id; 98 $outcome = new \grade_outcome($data, false); 99 $outcome->insert(); 100 101 $outcomegradeitem = new \grade_item(); 102 $outcomegradeitem->itemname = $outcome->shortname; 103 $outcomegradeitem->itemtype = 'mod'; 104 $outcomegradeitem->itemmodule = 'assign'; 105 $outcomegradeitem->iteminstance = $assignment->id; 106 $outcomegradeitem->outcomeid = $outcome->id; 107 $outcomegradeitem->cmid = 0; 108 $outcomegradeitem->courseid = $course->id; 109 $outcomegradeitem->aggregationcoef = 0; 110 $outcomegradeitem->itemnumber = 1; // The activity's original grade item will be 0. 111 $outcomegradeitem->gradetype = GRADE_TYPE_SCALE; 112 $outcomegradeitem->scaleid = $outcome->scaleid; 113 // This next two values for testing that returns parameters are correcly formatted. 114 $outcomegradeitem->set_locked(true); 115 $outcomegradeitem->hidden = ''; 116 $outcomegradeitem->insert(); 117 118 $assignmentgradeitem = \grade_item::fetch( 119 array( 120 'itemtype' => 'mod', 121 'itemmodule' => 'assign', 122 'iteminstance' => $assignment->id, 123 'itemnumber' => 0, 124 'courseid' => $course->id 125 ) 126 ); 127 $outcomegradeitem->set_parent($assignmentgradeitem->categoryid); 128 $outcomegradeitem->move_after_sortorder($assignmentgradeitem->sortorder); 129 130 return array($course, $assignment, $student1, $student2, $teacher, $parent); 131 } 132 133 /** 134 * Test update_grades() 135 */ 136 public function test_update_grades() { 137 global $DB; 138 139 $this->resetAfterTest(true); 140 141 $assignmentname = 'The assignment'; 142 $student1rawgrade = 10; 143 $student2rawgrade = 20; 144 list($course, $assignment, $student1, $student2, $teacher, $parent) = 145 $this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade); 146 $assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid, 0, false, MUST_EXIST); 147 148 $this->setUser($teacher); 149 150 // Teacher updating grade item information. 151 $changedmax = 93; 152 $result = core_grades_external::update_grades( 153 'test', 154 $course->id, 155 'mod_assign', 156 $assigmentcm->id, 157 0, 158 array(), 159 array('grademax' => $changedmax) 160 ); 161 $result = \external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result); 162 $this->assertTrue($result == GRADE_UPDATE_OK); 163 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id); 164 $this->assertTrue($grades->items[0]->grademax == $changedmax); 165 166 // Teacher updating 1 student grade. 167 $student1grade = 23; 168 $result = core_grades_external::update_grades( 169 'test', 170 $course->id, 171 'mod_assign', 172 $assigmentcm->id, 173 0, 174 array(array('studentid' => $student1->id, 'grade' => $student1grade)) 175 ); 176 $result = \external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result); 177 $this->assertTrue($result == GRADE_UPDATE_OK); 178 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id, array($student1->id)); 179 $this->assertTrue($grades->items[0]->grades[$student1->id]->grade == $student1grade); 180 181 // Teacher updating multiple student grades. 182 $student1grade = 11; 183 $student2grade = 13; 184 $result = core_grades_external::update_grades( 185 'test', 186 $course->id, 187 'mod_assign', 188 $assigmentcm->id, 189 0, 190 array( 191 array('studentid' => $student1->id, 'grade' => $student1grade), 192 array('studentid' => $student2->id, 'grade' => $student2grade) 193 ) 194 ); 195 $result = \external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result); 196 $this->assertTrue($result == GRADE_UPDATE_OK); 197 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id, array($student1->id, $student2->id)); 198 $this->assertTrue($grades->items[0]->grades[$student1->id]->grade == $student1grade); 199 $this->assertTrue($grades->items[0]->grades[$student2->id]->grade == $student2grade); 200 201 // Student attempting to update their own grade (should fail). 202 $this->setUser($student1); 203 try { 204 $student1grade = 17; 205 $result = core_grades_external::update_grades( 206 'test', 207 $course->id, 208 'mod_assign', 209 $assigmentcm->id, 210 0, 211 array( array('studentid' => $student1->id, 'grade' => $student1grade)) 212 ); 213 $this->fail('moodle_exception expected'); 214 } catch (\moodle_exception $ex) { 215 $this->assertTrue(true); 216 } 217 218 // Parent attempting to update their child's grade (should fail). 219 $this->setUser($parent); 220 try { 221 $student1grade = 13; 222 $result = core_grades_external::update_grades( 223 'test', 224 $course->id, 225 'mod_assign', 226 $assigmentcm->id, 227 0, 228 array( array('studentid' => $student1->id, 'grade' => $student1grade)) 229 ); 230 $this->fail('moodle_exception expected'); 231 } catch (\moodle_exception $ex) { 232 $this->assertTrue(true); 233 } 234 235 // Student trying to hide a grade item (should fail). 236 $this->setUser($student1); 237 try { 238 $result = core_grades_external::update_grades( 239 'test', 240 $course->id, 241 'mod_assign', 242 $assigmentcm->id, 243 0, 244 array(), 245 array('hidden' => 1) 246 ); 247 $this->fail('moodle_exception expected'); 248 } catch (\moodle_exception $ex) { 249 $this->assertTrue(true); 250 } 251 252 // Give the student role 'moodle/grade:hide' and they should now be able to hide the grade item. 253 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 254 $coursecontext = \context_course::instance($course->id); 255 assign_capability('moodle/grade:hide', CAP_ALLOW, $studentrole->id, $coursecontext->id); 256 accesslib_clear_all_caches_for_unit_testing(); 257 258 // Check the activity isn't already hidden. 259 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id); 260 $this->assertTrue($grades->items[0]->hidden == 0); 261 262 $result = core_grades_external::update_grades( 263 'test', 264 $course->id, 265 'mod_assign', 266 $assigmentcm->id, 267 0, 268 array(), 269 array('hidden' => 1) 270 ); 271 $result = \external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result); 272 $this->assertTrue($result == GRADE_UPDATE_OK); 273 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id); 274 $this->assertTrue($grades->items[0]->hidden == 1); 275 } 276 277 /** 278 * Test create_gradecategory. 279 * 280 * @return void 281 */ 282 public function test_create_gradecategory() { 283 global $DB; 284 $this->resetAfterTest(true); 285 $course = $this->getDataGenerator()->create_course(); 286 $this->setAdminUser(); 287 288 // Test the most basic gradecategory creation. 289 $status1 = core_grades_external::create_gradecategory($course->id, 'Test Category 1', []); 290 291 $courseparentcat = new \grade_category(['courseid' => $course->id, 'depth' => 1], true); 292 $record1 = $DB->get_record('grade_categories', ['id' => $status1['categoryid']]); 293 $this->assertEquals('Test Category 1', $record1->fullname); 294 // Confirm that the parent category for this category is the top level category for the course. 295 $this->assertEquals($courseparentcat->id, $record1->parent); 296 $this->assertEquals(2, $record1->depth); 297 298 // Now create a category as a child of the newly created category. 299 $status2 = core_grades_external::create_gradecategory($course->id, 'Test Category 2', ['parentcategoryid' => $record1->id]); 300 $record2 = $DB->get_record('grade_categories', ['id' => $status2['categoryid']]); 301 $this->assertEquals($record1->id, $record2->parent); 302 $this->assertEquals(3, $record2->depth); 303 // Check the path is correct. 304 $this->assertEquals('/' . implode('/', [$courseparentcat->id, $record1->id, $record2->id]) . '/', $record2->path); 305 306 // Now create a category with some customised data and check the returns. This customises every value. 307 $customopts = [ 308 'aggregation' => GRADE_AGGREGATE_MEAN, 309 'aggregateonlygraded' => 0, 310 'aggregateoutcomes' => 1, 311 'droplow' => 1, 312 'itemname' => 'item', 313 'iteminfo' => 'info', 314 'idnumber' => 'idnumber', 315 'gradetype' => GRADE_TYPE_TEXT, 316 'grademax' => 5, 317 'grademin' => 2, 318 'gradepass' => 3, 319 'display' => GRADE_DISPLAY_TYPE_LETTER, 320 // Hack. This must be -2 to use the default setting. 321 'decimals' => 3, 322 'hiddenuntil' => time(), 323 'locktime' => time(), 324 'weightoverride' => 1, 325 'aggregationcoef2' => 20, 326 'parentcategoryid' => $record2->id 327 ]; 328 329 $status3 = core_grades_external::create_gradecategory($course->id, 'Test Category 3', $customopts); 330 $cat3 = new \grade_category(['courseid' => $course->id, 'id' => $status3['categoryid']], true); 331 $cat3->load_grade_item(); 332 333 // Lets check all of the data is in the right shape. 334 $this->assertEquals(GRADE_AGGREGATE_MEAN, $cat3->aggregation); 335 $this->assertEquals(0, $cat3->aggregateonlygraded); 336 $this->assertEquals(1, $cat3->aggregateoutcomes); 337 $this->assertEquals(1, $cat3->droplow); 338 $this->assertEquals('item', $cat3->grade_item->itemname); 339 $this->assertEquals('info', $cat3->grade_item->iteminfo); 340 $this->assertEquals('idnumber', $cat3->grade_item->idnumber); 341 $this->assertEquals(GRADE_TYPE_TEXT, $cat3->grade_item->gradetype); 342 $this->assertEquals(5, $cat3->grade_item->grademax); 343 $this->assertEquals(2, $cat3->grade_item->grademin); 344 $this->assertEquals(3, $cat3->grade_item->gradepass); 345 $this->assertEquals(GRADE_DISPLAY_TYPE_LETTER, $cat3->grade_item->display); 346 $this->assertEquals(3, $cat3->grade_item->decimals); 347 $this->assertGreaterThanOrEqual($cat3->grade_item->hidden, time()); 348 $this->assertGreaterThanOrEqual($cat3->grade_item->locktime, time()); 349 $this->assertEquals(1, $cat3->grade_item->weightoverride); 350 // Coefficient is converted to percentage. 351 $this->assertEquals(0.2, $cat3->grade_item->aggregationcoef2); 352 $this->assertEquals($record2->id, $cat3->parent); 353 } 354 355 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body