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/>.
17 defined('MOODLE_INTERNAL') ||
die();
21 require_once($CFG->dirroot
. '/webservice/tests/helpers.php');
24 * Unit tests for the grading API defined in core_grading_external class.
26 * @package core_grading
28 * @copyright 2013 Paul Charsley
29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31 class core_grading_externallib_testcase
extends externallib_advanced_testcase
{
34 * Test get_definitions
36 public function test_get_definitions() {
37 global $DB, $CFG, $USER;
39 $this->resetAfterTest(true);
40 // Create a course and assignment.
41 $coursedata['idnumber'] = 'idnumbercourse';
42 $coursedata['fullname'] = 'Lightwork Course';
43 $coursedata['summary'] = 'Lightwork Course description';
44 $coursedata['summaryformat'] = FORMAT_MOODLE
;
45 $course = self
::getDataGenerator()->create_course($coursedata);
47 $assigndata['course'] = $course->id
;
48 $assigndata['name'] = 'lightwork assignment';
50 $cm = self
::getDataGenerator()->create_module('assign', $assigndata);
52 // Create manual enrolment record.
53 $manualenroldata['enrol'] = 'manual';
54 $manualenroldata['status'] = 0;
55 $manualenroldata['courseid'] = $course->id
;
56 $enrolid = $DB->insert_record('enrol', $manualenroldata);
58 // Create a teacher and give them capabilities.
59 $coursecontext = context_course
::instance($course->id
);
60 $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $coursecontext->id
, 3);
61 $modulecontext = context_module
::instance($cm->cmid
);
62 $this->assignUserCapability('mod/assign:grade', $modulecontext->id
, $roleid);
64 // Create the teacher's enrolment record.
65 $userenrolmentdata['status'] = 0;
66 $userenrolmentdata['enrolid'] = $enrolid;
67 $userenrolmentdata['userid'] = $USER->id
;
68 $DB->insert_record('user_enrolments', $userenrolmentdata);
70 // Create a grading area.
72 'contextid' => $modulecontext->id
,
73 'component' => 'mod_assign',
74 'areaname' => 'submissions',
75 'activemethod' => 'rubric'
77 $areaid = $DB->insert_record('grading_areas', $gradingarea);
79 // Create a rubric grading definition.
80 $rubricdefinition = array (
87 'usercreated' => $USER->id
,
89 'usermodified' => $USER->id
,
92 $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition);
94 // Create a criterion with levels.
95 $rubriccriteria1 = array (
96 'definitionid' => $definitionid,
98 'description' => 'Demonstrate an understanding of disease control',
99 'descriptionformat' => 0
101 $criterionid1 = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria1);
102 $rubriclevel1 = array (
103 'criterionid' => $criterionid1,
105 'definition' => 'pass',
106 'definitionformat' => 0
108 $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
109 $rubriclevel2 = array (
110 'criterionid' => $criterionid1,
112 'definition' => 'excellent',
113 'definitionformat' => 0
115 $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
117 // Create a second criterion with levels.
118 $rubriccriteria2 = array (
119 'definitionid' => $definitionid,
121 'description' => 'Demonstrate an understanding of brucellosis',
122 'descriptionformat' => 0
124 $criterionid2 = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria2);
125 $rubriclevel1 = array (
126 'criterionid' => $criterionid2,
128 'definition' => 'pass',
129 'definitionformat' => 0
131 $DB->insert_record('gradingform_rubric_levels', $rubriclevel1);
132 $rubriclevel2 = array (
133 'criterionid' => $criterionid2,
135 'definition' => 'excellent',
136 'definitionformat' => 0
138 $DB->insert_record('gradingform_rubric_levels', $rubriclevel2);
140 // Call the external function.
141 $cmids = array ($cm->cmid
);
142 $areaname = 'submissions';
143 $result = core_grading_external
::get_definitions($cmids, $areaname);
144 $result = external_api
::clean_returnvalue(core_grading_external
::get_definitions_returns(), $result);
146 $this->assertEquals(1, count($result['areas']));
147 $this->assertEquals(1, count($result['areas'][0]['definitions']));
148 $definition = $result['areas'][0]['definitions'][0];
150 $this->assertEquals($rubricdefinition['method'], $definition['method']);
151 $this->assertEquals($USER->id
, $definition['usercreated']);
153 require_once("$CFG->dirroot/grade/grading/lib.php");
154 require_once($CFG->dirroot
.'/grade/grading/form/'.$rubricdefinition['method'].'/lib.php');
156 $gradingmanager = get_grading_manager($areaid);
158 $this->assertEquals(1, count($definition[$rubricdefinition['method']]));
160 $rubricdetails = $definition[$rubricdefinition['method']];
161 $details = call_user_func('gradingform_'.$rubricdefinition['method'].'_controller::get_external_definition_details');
163 $this->assertEquals(2, count($rubricdetails[key($details)]));
166 foreach ($rubricdetails[key($details)] as $criterion) {
167 if ($criterion['id'] == $criterionid1) {
168 $this->assertEquals($rubriccriteria1['description'], $criterion['description']);
169 $this->assertEquals(2, count($criterion['levels']));
174 $this->assertTrue($found);
178 * Test get_gradingform_instances
180 public function test_get_gradingform_instances() {
183 $this->resetAfterTest(true);
184 // Create a course and assignment.
185 $coursedata['idnumber'] = 'idnumbercourse';
186 $coursedata['fullname'] = 'Lightwork Course';
187 $coursedata['summary'] = 'Lightwork Course description';
188 $coursedata['summaryformat'] = FORMAT_MOODLE
;
189 $course = self
::getDataGenerator()->create_course($coursedata);
191 $assigndata['course'] = $course->id
;
192 $assigndata['name'] = 'lightwork assignment';
194 $assign = self
::getDataGenerator()->create_module('assign', $assigndata);
196 // Create manual enrolment record.
197 $manualenroldata['enrol'] = 'manual';
198 $manualenroldata['status'] = 0;
199 $manualenroldata['courseid'] = $course->id
;
200 $enrolid = $DB->insert_record('enrol', $manualenroldata);
202 // Create a teacher and give them capabilities.
203 $coursecontext = context_course
::instance($course->id
);
204 $roleid = $this->assignUserCapability('moodle/course:viewparticipants', $coursecontext->id
, 3);
205 $modulecontext = context_module
::instance($assign->cmid
);
206 $this->assignUserCapability('mod/assign:grade', $modulecontext->id
, $roleid);
208 // Create the teacher's enrolment record.
209 $userenrolmentdata['status'] = 0;
210 $userenrolmentdata['enrolid'] = $enrolid;
211 $userenrolmentdata['userid'] = $USER->id
;
212 $DB->insert_record('user_enrolments', $userenrolmentdata);
214 // Create a student with an assignment grade.
215 $student = self
::getDataGenerator()->create_user();
216 $assigngrade = new stdClass();
217 $assigngrade->assignment
= $assign->id
;
218 $assigngrade->userid
= $student->id
;
219 $assigngrade->timecreated
= time();
220 $assigngrade->timemodified
= $assigngrade->timecreated
;
221 $assigngrade->grader
= $USER->id
;
222 $assigngrade->grade
= 50;
223 $assigngrade->attemptnumber
= 0;
224 $gid = $DB->insert_record('assign_grades', $assigngrade);
226 // Create a grading area.
227 $gradingarea = array(
228 'contextid' => $modulecontext->id
,
229 'component' => 'mod_assign',
230 'areaname' => 'submissions',
231 'activemethod' => 'rubric'
233 $areaid = $DB->insert_record('grading_areas', $gradingarea);
235 // Create a rubric grading definition.
236 $rubricdefinition = array (
238 'method' => 'rubric',
243 'usercreated' => $USER->id
,
245 'usermodified' => $USER->id
,
248 $definitionid = $DB->insert_record('grading_definitions', $rubricdefinition);
250 // Create a criterion with a level.
251 $rubriccriteria = array (
252 'definitionid' => $definitionid,
254 'description' => 'Demonstrate an understanding of disease control',
255 'descriptionformat' => 0
257 $criterionid = $DB->insert_record('gradingform_rubric_criteria', $rubriccriteria);
258 $rubriclevel = array (
259 'criterionid' => $criterionid,
261 'definition' => 'pass',
262 'definitionformat' => 0
264 $levelid = $DB->insert_record('gradingform_rubric_levels', $rubriclevel);
266 // Create a grading instance.
268 'definitionid' => $definitionid,
269 'raterid' => $USER->id
,
272 'feedbackformat' => 0,
275 $instanceid = $DB->insert_record('grading_instances', $instance);
279 'instanceid' => $instanceid,
280 'criterionid' => $criterionid,
281 'levelid' => $levelid,
282 'remark' => 'excellent work',
285 $DB->insert_record('gradingform_rubric_fillings', $filling);
287 // Call the external function.
288 $result = core_grading_external
::get_gradingform_instances($definitionid, 0);
289 $result = external_api
::clean_returnvalue(core_grading_external
::get_gradingform_instances_returns(), $result);
291 $this->assertEquals(1, count($result['instances']));
292 $this->assertEquals($USER->id
, $result['instances'][0]['raterid']);
293 $this->assertEquals($gid, $result['instances'][0]['itemid']);
294 $this->assertEquals(1, $result['instances'][0]['status']);
295 $this->assertEquals(1, $result['instances'][0]['timemodified']);
296 $this->assertEquals(1, count($result['instances'][0]['rubric']));
297 $this->assertEquals(1, count($result['instances'][0]['rubric']['criteria']));
298 $criteria = $result['instances'][0]['rubric']['criteria'];
299 $this->assertEquals($criterionid, $criteria[0]['criterionid']);
300 $this->assertEquals($levelid, $criteria[0]['levelid']);
301 $this->assertEquals('excellent work', $criteria[0]['remark']);
306 * Test save_definitions for rubric grading method
308 public function test_save_definitions_rubric() {
309 global $DB, $CFG, $USER;
311 $this->resetAfterTest(true);
312 // Create a course and assignment.
313 $course = self
::getDataGenerator()->create_course();
314 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
315 $params['course'] = $course->id
;
316 $instance = $generator->create_instance($params);
317 $cm = get_coursemodule_from_instance('assign', $instance->id
);
318 $context = context_module
::instance($cm->id
);
319 $coursecontext = context_course
::instance($course->id
);
321 // Create the teacher.
322 $teacher = self
::getDataGenerator()->create_user();
323 $USER->id
= $teacher->id
;
324 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
325 $this->assignUserCapability('moodle/grade:managegradingforms', $context->id
, $teacherrole->id
);
326 $this->getDataGenerator()->enrol_user($teacher->id
,
330 // The grading area to insert.
331 $gradingarea = array(
333 'contextid' => $context->id
,
334 'component' => 'mod_assign',
335 'areaname' => 'submissions',
336 'activemethod' => 'rubric'
339 // The rubric definition to insert.
340 $rubricdefinition = array(
341 'method' => 'rubric',
347 'usercreated' => $teacher->id
,
349 'usermodified' => $teacher->id
,
353 // The criterion to insert.
354 $rubriccriteria1 = array (
356 'description' => 'Demonstrate an understanding of disease control',
357 'descriptionformat' => 0
360 // 3 levels for the criterion.
361 $rubriclevel1 = array (
363 'definition' => 'pass',
364 'definitionformat' => 0
366 $rubriclevel2 = array (
368 'definition' => 'excellent',
369 'definitionformat' => 0
371 $rubriclevel3 = array (
373 'definition' => 'fail',
374 'definitionformat' => 0
377 $rubriccriteria1['levels'] = array($rubriclevel1, $rubriclevel2, $rubriclevel3);
378 $rubricdefinition['rubric'] = array('rubric_criteria' => array($rubriccriteria1));
379 $gradingarea['definitions'] = array($rubricdefinition);
381 $results = core_grading_external
::save_definitions(array($gradingarea));
383 $area = $DB->get_record('grading_areas',
384 array('contextid' => $context->id
, 'component' => 'mod_assign', 'areaname' => 'submissions'),
386 $this->assertEquals($area->activemethod
, 'rubric');
388 $definition = $DB->get_record('grading_definitions', array('areaid' => $area->id
, 'method' => 'rubric'), '*', MUST_EXIST
);
389 $this->assertEquals($rubricdefinition['name'], $definition->name
);
391 $criterion1 = $DB->get_record('gradingform_rubric_criteria', array('definitionid' => $definition->id
), '*', MUST_EXIST
);
392 $levels = $DB->get_records('gradingform_rubric_levels', array('criterionid' => $criterion1->id
));
393 $validlevelcount = 0;
394 $expectedvalue = true;
395 foreach ($levels as $level) {
396 if ($level->score
== 0) {
397 $this->assertEquals('fail', $level->definition
);
399 } else if ($level->score
== 50) {
400 $this->assertEquals('pass', $level->definition
);
402 } else if ($level->score
== 100) {
403 $this->assertEquals('excellent', $level->definition
);
404 $excellentlevelid = $level->id
;
407 $expectedvalue = false;
410 $this->assertEquals(3, $validlevelcount);
411 $this->assertTrue($expectedvalue, 'A level with an unexpected score was found');
413 // Test add a new level and modify an existing.
414 // Test add a new criteria and modify an existing.
415 // Test modify a definition.
417 // The rubric definition to update.
418 $rubricdefinition = array(
419 'id' => $definition->id
,
420 'method' => 'rubric',
421 'name' => 'test changed',
426 'usercreated' => $teacher->id
,
428 'usermodified' => $teacher->id
,
432 // A criterion to update.
433 $rubriccriteria1 = array (
434 'id' => $criterion1->id
,
436 'description' => 'Demonstrate an understanding of rabies control',
437 'descriptionformat' => 0
440 // A new criterion to add.
441 $rubriccriteria2 = array (
443 'description' => 'Demonstrate an understanding of anthrax control',
444 'descriptionformat' => 0
447 // A level to update.
448 $rubriclevel2 = array (
449 'id' => $excellentlevelid,
451 'definition' => 'excellent',
452 'definitionformat' => 0
455 // A level to insert.
456 $rubriclevel4 = array (
458 'definition' => 'superb',
459 'definitionformat' => 0
462 $rubriccriteria1['levels'] = array($rubriclevel1, $rubriclevel2, $rubriclevel3, $rubriclevel4);
463 $rubricdefinition['rubric'] = array('rubric_criteria' => array($rubriccriteria1, $rubriccriteria2));
464 $gradingarea['definitions'] = array($rubricdefinition);
466 $results = core_grading_external
::save_definitions(array($gradingarea));
468 // Test definition name change.
469 $definition = $DB->get_record('grading_definitions', array('id' => $definition->id
), '*', MUST_EXIST
);
470 $this->assertEquals('test changed', $definition->name
);
472 // Test criteria description change.
473 $modifiedcriteria = $DB->get_record('gradingform_rubric_criteria', array('id' => $criterion1->id
), '*', MUST_EXIST
);
474 $this->assertEquals('Demonstrate an understanding of rabies control', $modifiedcriteria->description
);
476 // Test new criteria added.
477 $newcriteria = $DB->get_record('gradingform_rubric_criteria',
478 array('definitionid' => $definition->id
, 'sortorder' => 2), '*', MUST_EXIST
);
479 $this->assertEquals('Demonstrate an understanding of anthrax control', $newcriteria->description
);
481 // Test excellent level score change from 100 to 75.
482 $modifiedlevel = $DB->get_record('gradingform_rubric_levels', array('id' => $excellentlevelid), '*', MUST_EXIST
);
483 $this->assertEquals(75, $modifiedlevel->score
);
485 // Test new superb level added.
486 $newlevel = $DB->get_record('gradingform_rubric_levels',
487 array('criterionid' => $criterion1->id
, 'score' => 100), '*', MUST_EXIST
);
488 $this->assertEquals('superb', $newlevel->definition
);
490 // Test remove a level
491 // Test remove a criterion
492 // The rubric definition with the removed criterion and levels.
493 $rubricdefinition = array(
494 'id' => $definition->id
,
495 'method' => 'rubric',
496 'name' => 'test changed',
501 'usercreated' => $teacher->id
,
503 'usermodified' => $teacher->id
,
507 $rubriccriteria1 = array (
508 'id' => $criterion1->id
,
510 'description' => 'Demonstrate an understanding of rabies control',
511 'descriptionformat' => 0
514 $rubriclevel1 = array (
516 'definition' => 'fail',
517 'definitionformat' => 0
519 $rubriclevel2 = array (
521 'definition' => 'pass',
522 'definitionformat' => 0
525 $rubriccriteria1['levels'] = array($rubriclevel1, $rubriclevel2);
526 $rubricdefinition['rubric'] = array('rubric_criteria' => array($rubriccriteria1));
527 $gradingarea['definitions'] = array($rubricdefinition);
529 $results = core_grading_external
::save_definitions(array($gradingarea));
531 // Only 1 criterion should now exist.
532 $this->assertEquals(1, $DB->count_records('gradingform_rubric_criteria', array('definitionid' => $definition->id
)));
533 $criterion1 = $DB->get_record('gradingform_rubric_criteria', array('definitionid' => $definition->id
), '*', MUST_EXIST
);
534 $this->assertEquals('Demonstrate an understanding of rabies control', $criterion1->description
);
535 // This criterion should only have 2 levels.
536 $this->assertEquals(2, $DB->count_records('gradingform_rubric_levels', array('criterionid' => $criterion1->id
)));
538 $gradingarea['activemethod'] = 'invalid';
539 $this->setExpectedException('moodle_exception');
540 $results = core_grading_external
::save_definitions(array($gradingarea));
545 * Tests save_definitions for the marking guide grading method
547 public function test_save_definitions_marking_guide() {
548 global $DB, $CFG, $USER;
550 $this->resetAfterTest(true);
551 // Create a course and assignment.
552 $course = self
::getDataGenerator()->create_course();
553 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
554 $params['course'] = $course->id
;
555 $instance = $generator->create_instance($params);
556 $cm = get_coursemodule_from_instance('assign', $instance->id
);
557 $context = context_module
::instance($cm->id
);
558 $coursecontext = context_course
::instance($course->id
);
560 // Create the teacher.
561 $teacher = self
::getDataGenerator()->create_user();
562 $USER->id
= $teacher->id
;
563 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
564 $this->assignUserCapability('moodle/grade:managegradingforms', $context->id
, $teacherrole->id
);
565 $this->getDataGenerator()->enrol_user($teacher->id
,
569 // Test insert a grading area with guide definition, criteria and comments.
570 $gradingarea = array(
572 'contextid' => $context->id
,
573 'component' => 'mod_assign',
574 'areaname' => 'submissions',
575 'activemethod' => 'guide'
578 $guidedefinition = array(
585 'usercreated' => $teacher->id
,
587 'usermodified' => $teacher->id
,
591 $guidecomment = array(
593 'description' => 'Students need to show that they understand the control of zoonoses',
594 'descriptionformat' => 0
596 $guidecriteria1 = array (
598 'shortname' => 'Rabies Control',
599 'description' => 'Understand rabies control techniques',
600 'descriptionformat' => 0,
601 'descriptionmarkers' => 'Student must demonstrate that they understand rabies control',
602 'descriptionmarkersformat' => 0,
605 $guidecriteria2 = array (
607 'shortname' => 'Anthrax Control',
608 'description' => 'Understand anthrax control',
609 'descriptionformat' => 0,
610 'descriptionmarkers' => 'Student must demonstrate that they understand anthrax control',
611 'descriptionmarkersformat' => 0,
615 $guidedefinition['guide'] = array('guide_criteria' => array($guidecriteria1, $guidecriteria2),
616 'guide_comments' => array($guidecomment));
617 $gradingarea['definitions'] = array($guidedefinition);
619 $results = core_grading_external
::save_definitions(array($gradingarea));
620 $area = $DB->get_record('grading_areas',
621 array('contextid' => $context->id
, 'component' => 'mod_assign', 'areaname' => 'submissions'),
623 $this->assertEquals($area->activemethod
, 'guide');
625 $definition = $DB->get_record('grading_definitions', array('areaid' => $area->id
, 'method' => 'guide'), '*', MUST_EXIST
);
626 $this->assertEquals($guidedefinition['name'], $definition->name
);
627 $this->assertEquals(2, $DB->count_records('gradingform_guide_criteria', array('definitionid' => $definition->id
)));
628 $this->assertEquals(1, $DB->count_records('gradingform_guide_comments', array('definitionid' => $definition->id
)));
630 // Test removal of a criteria.
631 $guidedefinition['guide'] = array('guide_criteria' => array($guidecriteria1),
632 'guide_comments' => array($guidecomment));
633 $gradingarea['definitions'] = array($guidedefinition);
634 $results = core_grading_external
::save_definitions(array($gradingarea));
635 $this->assertEquals(1, $DB->count_records('gradingform_guide_criteria', array('definitionid' => $definition->id
)));
637 // Test an invalid method in the definition.
638 $guidedefinition['method'] = 'invalid';
639 $gradingarea['definitions'] = array($guidedefinition);
640 $this->setExpectedException('invalid_parameter_exception');
641 $results = core_grading_external
::save_definitions(array($gradingarea));