2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Unit tests for the grade API at /lib/classes/grades_external.php
20 * @package core_grades
22 * @copyright 2012 Andrew Davis
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') ||
die();
31 require_once($CFG->dirroot
. '/webservice/tests/helpers.php');
34 * Grades functions unit tests
36 * @package core_grades
38 * @copyright 2012 Andrew Davis
39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41 class core_grades_external_testcase
extends externallib_advanced_testcase
{
44 * Load initial test information
46 * @param string $assignmentname Assignment name
47 * @param int $student1rawgrade Student 1 grade
48 * @param int $student2rawgrade Student 2 grade
49 * @return array Array of vars with test information
51 protected function load_test_data($assignmentname, $student1rawgrade, $student2rawgrade) {
54 // Adds a course, a teacher, 2 students, an assignment and grades for the students.
55 $course = $this->getDataGenerator()->create_course();
56 $coursecontext = context_course
::instance($course->id
);
58 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
60 $student1 = $this->getDataGenerator()->create_user();
61 $this->getDataGenerator()->enrol_user($student1->id
, $course->id
, $studentrole->id
);
63 $student2 = $this->getDataGenerator()->create_user();
64 $this->getDataGenerator()->enrol_user($student2->id
, $course->id
, $studentrole->id
);
66 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
67 $teacher = $this->getDataGenerator()->create_user();
68 $this->getDataGenerator()->enrol_user($teacher->id
, $course->id
, $teacherrole->id
);
70 $parent = $this->getDataGenerator()->create_user();
71 $this->setUser($parent);
72 $student1context = context_user
::instance($student1->id
);
73 // Creates a new role, gives it the capability and gives $USER that role.
74 $parentroleid = $this->assignUserCapability('moodle/grade:viewall', $student1context->id
);
75 // Enrol the user in the course using the new role.
76 $this->getDataGenerator()->enrol_user($parent->id
, $course->id
, $parentroleid);
78 $assignment = $this->getDataGenerator()->create_module('assign', array('name' => $assignmentname, 'course' => $course->id
));
79 $modcontext = get_coursemodule_from_instance('assign', $assignment->id
, $course->id
);
80 $assignment->cmidnumber
= $modcontext->id
;
82 $student1grade = array('userid' => $student1->id
, 'rawgrade' => $student1rawgrade);
83 $student2grade = array('userid' => $student2->id
, 'rawgrade' => $student2rawgrade);
84 $studentgrades = array($student1->id
=> $student1grade, $student2->id
=> $student2grade);
85 assign_grade_item_update($assignment, $studentgrades);
87 // Insert a custom grade scale to be used by an outcome.
88 $gradescale = new grade_scale();
89 $gradescale->name
= 'unittestscale3';
90 $gradescale->courseid
= $course->id
;
91 $gradescale->userid
= 0;
92 $gradescale->scale
= 'Distinction, Very Good, Good, Pass, Fail';
93 $gradescale->description
= 'This scale is used to mark standard assignments.';
94 $gradescale->insert();
97 $data = new stdClass();
98 $data->courseid
= $course->id
;
99 $data->fullname
= 'Team work';
100 $data->shortname
= 'Team work';
101 $data->scaleid
= $gradescale->id
;
102 $outcome = new grade_outcome($data, false);
105 $outcomegradeitem = new grade_item();
106 $outcomegradeitem->itemname
= $outcome->shortname
;
107 $outcomegradeitem->itemtype
= 'mod';
108 $outcomegradeitem->itemmodule
= 'assign';
109 $outcomegradeitem->iteminstance
= $assignment->id
;
110 $outcomegradeitem->outcomeid
= $outcome->id
;
111 $outcomegradeitem->cmid
= 0;
112 $outcomegradeitem->courseid
= $course->id
;
113 $outcomegradeitem->aggregationcoef
= 0;
114 $outcomegradeitem->itemnumber
= 1; // The activity's original grade item will be 0.
115 $outcomegradeitem->gradetype
= GRADE_TYPE_SCALE
;
116 $outcomegradeitem->scaleid
= $outcome->scaleid
;
117 // This next two values for testing that returns parameters are correcly formatted.
118 $outcomegradeitem->set_locked(true);
119 $outcomegradeitem->hidden
= '';
120 $outcomegradeitem->insert();
122 $assignmentgradeitem = grade_item
::fetch(
125 'itemmodule' => 'assign',
126 'iteminstance' => $assignment->id
,
128 'courseid' => $course->id
131 $outcomegradeitem->set_parent($assignmentgradeitem->categoryid
);
132 $outcomegradeitem->move_after_sortorder($assignmentgradeitem->sortorder
);
134 return array($course, $assignment, $student1, $student2, $teacher, $parent);
140 public function test_get_grades() {
143 $this->resetAfterTest(true);
144 $CFG->enableoutcomes
= 1;
146 $assignmentname = 'The assignment';
147 $student1rawgrade = 10;
148 $student2rawgrade = 20;
149 list($course, $assignment, $student1, $student2, $teacher, $parent) =
150 $this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade);
151 $assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid
, 0, false, MUST_EXIST
);
153 // Teacher requesting a student grade for the assignment.
154 $this->setUser($teacher);
155 $grades = core_grades_external
::get_grades(
161 $grades = external_api
::clean_returnvalue(core_grades_external
::get_grades_returns(), $grades);
162 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id
, $student1->id
));
164 // Teacher requesting all the grades of student1 in a course.
165 $grades = core_grades_external
::get_grades(
171 $grades = external_api
::clean_returnvalue(core_grades_external
::get_grades_returns(), $grades);
172 $this->assertTrue(count($grades['items']) == 2);
173 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id
, $student1->id
));
174 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, 'course', $student1->id
));
176 $outcome = $this->get_outcome($grades, $assigmentcm->id
);
177 $this->assertEquals($outcome['name'], 'Team work');
178 $this->assertEquals(0, $this->get_outcome_student_grade($grades, $assigmentcm->id
, $student1->id
));
180 // Teacher requesting all the grades of all the students in a course.
181 $grades = core_grades_external
::get_grades(
185 array($student1->id
, $student2->id
)
187 $grades = external_api
::clean_returnvalue(core_grades_external
::get_grades_returns(), $grades);
188 $this->assertTrue(count($grades['items']) == 2);
189 $this->assertTrue(count($grades['items'][0]['grades']) == 2);
190 $this->assertTrue(count($grades['items'][1]['grades']) == 2);
192 // Student requesting another student's grade for the assignment (should fail).
193 $this->setUser($student1);
195 $grades = core_grades_external
::get_grades(
201 $this->fail('moodle_exception expected');
202 } catch (moodle_exception
$ex) {
203 $this->assertTrue(true);
206 // Parent requesting their child's grade for the assignment (should fail).
207 $this->setUser($parent);
209 $grades = core_grades_external
::get_grades(
215 $this->fail('moodle_exception expected');
216 } catch (moodle_exception
$ex) {
217 $this->assertTrue(true);
220 // Parent requesting another student's grade for the assignment(should fail).
222 $grades = core_grades_external
::get_grades(
228 $this->fail('moodle_exception expected');
229 } catch (moodle_exception
$ex) {
230 $this->assertTrue(true);
233 // Student requesting all other student grades for the assignment (should fail).
235 $grades = core_grades_external
::get_grades(
239 array($student1->id
, $student2->id
)
241 $this->fail('moodle_exception expected');
242 } catch (moodle_exception
$ex) {
243 $this->assertTrue(true);
246 // Student requesting only grade item information (should fail).
248 $grades = core_grades_external
::get_grades(
254 $this->fail('moodle_exception expected');
255 } catch (moodle_exception
$ex) {
256 $this->assertTrue(true);
259 // Teacher requesting student grades for a course.
260 $this->setUser($teacher);
261 $grades = core_grades_external
::get_grades(
265 array($student1->id
, $student2->id
)
267 $grades = external_api
::clean_returnvalue(core_grades_external
::get_grades_returns(), $grades);
268 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id
, $student1->id
));
269 $this->assertEquals($student2rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id
, $student2->id
));
271 // Teacher requesting grade item information.
272 $grades = core_grades_external
::get_grades(
277 $grades = external_api
::clean_returnvalue(core_grades_external
::get_grades_returns(), $grades);
278 $activity = $this->get_activity($grades, $assigmentcm->id
);
279 $this->assertEquals($activity['name'], $assignmentname);
280 $this->assertEquals(count($activity['grades']), 0);
282 // Teacher requesting all grade items in a course.
283 $grades = core_grades_external
::get_grades(
286 $grades = external_api
::clean_returnvalue(core_grades_external
::get_grades_returns(), $grades);
287 $this->assertTrue(count($grades['items']) == 2);
289 $activity = $this->get_activity($grades, $assigmentcm->id
);
290 $this->assertEquals($activity['name'], $assignmentname);
291 $this->assertEquals(count($activity['grades']), 0);
293 $outcome = $this->get_outcome($grades, $assigmentcm->id
);
294 $this->assertEquals($outcome['name'], 'Team work');
296 // Hide a grade item then have student request it.
297 $result = core_grades_external
::update_grades(
306 $result = external_api
::clean_returnvalue(core_grades_external
::update_grades_returns(), $result);
307 $this->assertTrue($result == GRADE_UPDATE_OK
);
309 // Check it's definitely hidden.
310 $grades = grade_get_grades($course->id
, 'mod', 'assign', $assignment->id
);
311 $this->assertEquals($grades->items
[0]->hidden
, 1);
313 // Teacher should still be able to see the hidden grades.
314 $this->setUser($teacher);
315 $grades = core_grades_external
::get_grades(
321 $grades = external_api
::clean_returnvalue(core_grades_external
::get_grades_returns(), $grades);
322 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id
, $student1->id
));
328 * @param array $grades Array of grades
329 * @param int $cmid Activity course module id
330 * @return strdClass Activity object
332 private function get_activity($grades, $cmid) {
333 foreach ($grades['items'] as $item) {
334 if ($item['activityid'] == $cmid) {
342 * Get a grade for an activity
344 * @param array $grades Array of grades
345 * @param int $cmid Activity course module id
346 * @param int $studentid Student it
347 * @return stdClass Activity Object
349 private function get_activity_student_grade($grades, $cmid, $studentid) {
350 $item = $this->get_activity($grades, $cmid);
351 foreach ($item['grades'] as $grade) {
352 if ($grade['userid'] == $studentid) {
353 return $grade['grade'];
362 * @param array $grades Array of grades
363 * @param int $cmid Activity course module id
364 * @return stdClass Outcome object
366 private function get_outcome($grades, $cmid) {
367 foreach ($grades['outcomes'] as $outcome) {
368 if ($outcome['activityid'] == $cmid) {
376 * Get a grade from an outcome
378 * @param array $grades Array of grades
379 * @param int $cmid Activity course module id
380 * @param int $studentid Student id
381 * @return stdClass Outcome object
383 private function get_outcome_student_grade($grades, $cmid, $studentid) {
384 $outcome = $this->get_outcome($grades, $cmid);
385 foreach ($outcome['grades'] as $grade) {
386 if ($grade['userid'] == $studentid) {
387 return $grade['grade'];
394 * Test update_grades()
396 public function test_update_grades() {
399 $this->resetAfterTest(true);
401 $assignmentname = 'The assignment';
402 $student1rawgrade = 10;
403 $student2rawgrade = 20;
404 list($course, $assignment, $student1, $student2, $teacher, $parent) =
405 $this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade);
406 $assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid
, 0, false, MUST_EXIST
);
408 $this->setUser($teacher);
410 // Teacher updating grade item information.
412 $result = core_grades_external
::update_grades(
419 array('grademax' => $changedmax)
421 $result = external_api
::clean_returnvalue(core_grades_external
::update_grades_returns(), $result);
422 $this->assertTrue($result == GRADE_UPDATE_OK
);
423 $grades = grade_get_grades($course->id
, 'mod', 'assign', $assignment->id
);
424 $this->assertTrue($grades->items
[0]->grademax
== $changedmax);
426 // Teacher updating 1 student grade.
428 $result = core_grades_external
::update_grades(
434 array(array('studentid' => $student1->id
, 'grade' => $student1grade))
436 $result = external_api
::clean_returnvalue(core_grades_external
::update_grades_returns(), $result);
437 $this->assertTrue($result == GRADE_UPDATE_OK
);
438 $grades = grade_get_grades($course->id
, 'mod', 'assign', $assignment->id
, array($student1->id
));
439 $this->assertTrue($grades->items
[0]->grades
[$student1->id
]->grade
== $student1grade);
441 // Teacher updating multiple student grades.
444 $result = core_grades_external
::update_grades(
451 array('studentid' => $student1->id
, 'grade' => $student1grade),
452 array('studentid' => $student2->id
, 'grade' => $student2grade)
455 $result = external_api
::clean_returnvalue(core_grades_external
::update_grades_returns(), $result);
456 $this->assertTrue($result == GRADE_UPDATE_OK
);
457 $grades = grade_get_grades($course->id
, 'mod', 'assign', $assignment->id
, array($student1->id
, $student2->id
));
458 $this->assertTrue($grades->items
[0]->grades
[$student1->id
]->grade
== $student1grade);
459 $this->assertTrue($grades->items
[0]->grades
[$student2->id
]->grade
== $student2grade);
461 // Student attempting to update their own grade (should fail).
462 $this->setUser($student1);
465 $result = core_grades_external
::update_grades(
471 array( array('studentid' => $student1->id
, 'grade' => $student1grade))
473 $this->fail('moodle_exception expected');
474 } catch (moodle_exception
$ex) {
475 $this->assertTrue(true);
478 // Parent attempting to update their child's grade (should fail).
479 $this->setUser($parent);
482 $result = core_grades_external
::update_grades(
488 array( array('studentid' => $student1->id
, 'grade' => $student1grade))
490 $this->fail('moodle_exception expected');
491 } catch (moodle_exception
$ex) {
492 $this->assertTrue(true);
495 // Student trying to hide a grade item (should fail).
496 $this->setUser($student1);
498 $result = core_grades_external
::update_grades(
507 $this->fail('moodle_exception expected');
508 } catch (moodle_exception
$ex) {
509 $this->assertTrue(true);
512 // Give the student role 'moodle/grade:hide' and they should now be able to hide the grade item.
513 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
514 $coursecontext = context_course
::instance($course->id
);
515 assign_capability('moodle/grade:hide', CAP_ALLOW
, $studentrole->id
, $coursecontext->id
);
516 accesslib_clear_all_caches_for_unit_testing();
518 // Check the activity isn't already hidden.
519 $grades = grade_get_grades($course->id
, 'mod', 'assign', $assignment->id
);
520 $this->assertTrue($grades->items
[0]->hidden
== 0);
522 $result = core_grades_external
::update_grades(
531 $result = external_api
::clean_returnvalue(core_grades_external
::update_grades_returns(), $result);
532 $this->assertTrue($result == GRADE_UPDATE_OK
);
533 $grades = grade_get_grades($course->id
, 'mod', 'assign', $assignment->id
);
534 $this->assertTrue($grades->items
[0]->hidden
== 1);