Rev 1 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** Unit tests for grade/lib.php.** @package core_grades* @category test* @copyright 2016 Jun Pataleta* @license http://www.gnu.org/copyleft/gpl.html GNU Public License*/namespace core_grades;use assign;use cm_info;use grade_item;use grade_plugin_return;use grade_report_grader;defined('MOODLE_INTERNAL') || die();global $CFG;require_once($CFG->dirroot . '/grade/lib.php');/*** Unit tests for grade/lib.php.** @package core_grades* @category test* @copyright 2016 Jun Pataleta <jun@moodle.com>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class lib_test extends \advanced_testcase {/*** Test can_output_item.*/public function test_can_output_item(): void {$this->resetAfterTest();$generator = $this->getDataGenerator();// Course level grade category.$course = $generator->create_course();// Grade tree looks something like:// - Test course (Rendered).$gradetree = \grade_category::fetch_course_tree($course->id);$this->assertTrue(\grade_tree::can_output_item($gradetree));// Add a grade category with default settings.$generator->create_grade_category(array('courseid' => $course->id));// Grade tree now looks something like:// - Test course n (Rendered).// -- Grade category n (Rendered).$gradetree = \grade_category::fetch_course_tree($course->id);$this->assertNotEmpty($gradetree['children']);foreach ($gradetree['children'] as $child) {$this->assertTrue(\grade_tree::can_output_item($child));}// Add a grade category with grade type = None.$nototalcategory = 'No total category';$nototalparams = ['courseid' => $course->id,'fullname' => $nototalcategory,'aggregation' => GRADE_AGGREGATE_WEIGHTED_MEAN];$nototal = $generator->create_grade_category($nototalparams);$catnototal = \grade_category::fetch(array('id' => $nototal->id));// Set the grade type of the grade item associated to the grade category.$catitemnototal = $catnototal->load_grade_item();$catitemnototal->gradetype = GRADE_TYPE_NONE;$catitemnototal->update();// Grade tree looks something like:// - Test course n (Rendered).// -- Grade category n (Rendered).// -- No total category (Not rendered).$gradetree = \grade_category::fetch_course_tree($course->id);foreach ($gradetree['children'] as $child) {if ($child['object']->fullname == $nototalcategory) {$this->assertFalse(\grade_tree::can_output_item($child));} else {$this->assertTrue(\grade_tree::can_output_item($child));}}// Add another grade category with default settings under 'No total category'.$normalinnototalparams = ['courseid' => $course->id,'fullname' => 'Normal category in no total category','parent' => $nototal->id];$generator->create_grade_category($normalinnototalparams);// Grade tree looks something like:// - Test course n (Rendered).// -- Grade category n (Rendered).// -- No total category (Rendered).// --- Normal category in no total category (Rendered).$gradetree = \grade_category::fetch_course_tree($course->id);foreach ($gradetree['children'] as $child) {// All children are now visible.$this->assertTrue(\grade_tree::can_output_item($child));if (!empty($child['children'])) {foreach ($child['children'] as $grandchild) {$this->assertTrue(\grade_tree::can_output_item($grandchild));}}}// Add a grade category with grade type = None.$nototalcategory2 = 'No total category 2';$nototal2params = ['courseid' => $course->id,'fullname' => $nototalcategory2,'aggregation' => GRADE_AGGREGATE_WEIGHTED_MEAN];$nototal2 = $generator->create_grade_category($nototal2params);$catnototal2 = \grade_category::fetch(array('id' => $nototal2->id));// Set the grade type of the grade item associated to the grade category.$catitemnototal2 = $catnototal2->load_grade_item();$catitemnototal2->gradetype = GRADE_TYPE_NONE;$catitemnototal2->update();// Add a category with no total under 'No total category'.$nototalinnototalcategory = 'Category with no total in no total category';$nototalinnototalparams = ['courseid' => $course->id,'fullname' => $nototalinnototalcategory,'aggregation' => GRADE_AGGREGATE_WEIGHTED_MEAN,'parent' => $nototal2->id];$nototalinnototal = $generator->create_grade_category($nototalinnototalparams);$catnototalinnototal = \grade_category::fetch(array('id' => $nototalinnototal->id));// Set the grade type of the grade item associated to the grade category.$catitemnototalinnototal = $catnototalinnototal->load_grade_item();$catitemnototalinnototal->gradetype = GRADE_TYPE_NONE;$catitemnototalinnototal->update();// Grade tree looks something like:// - Test course n (Rendered).// -- Grade category n (Rendered).// -- No total category (Rendered).// --- Normal category in no total category (Rendered).// -- No total category 2 (Not rendered).// --- Category with no total in no total category (Not rendered).$gradetree = \grade_category::fetch_course_tree($course->id);foreach ($gradetree['children'] as $child) {if ($child['object']->fullname == $nototalcategory2) {$this->assertFalse(\grade_tree::can_output_item($child));} else {$this->assertTrue(\grade_tree::can_output_item($child));}if (!empty($child['children'])) {foreach ($child['children'] as $grandchild) {if ($grandchild['object']->fullname == $nototalinnototalcategory) {$this->assertFalse(\grade_tree::can_output_item($grandchild));} else {$this->assertTrue(\grade_tree::can_output_item($grandchild));}}}}}/*** Tests that ungraded_counts calculates count and sum of grades correctly when there are graded users.** @covers \grade_report::ungraded_counts*/public function test_ungraded_counts_count_sumgrades(): void {global $DB;$this->resetAfterTest(true);$course1 = $this->getDataGenerator()->create_course();$course2 = $this->getDataGenerator()->create_course();$studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);$teacherrole = $DB->get_record('role', ['shortname' => 'editingteacher'], '*', MUST_EXIST);// Custom roles (gradable and non gradable).$gradeblerole = create_role('New student role', 'gradable','Gradable role', 'student');$nongradeblerole = create_role('New student role', 'nongradable','Non gradable role', 'student');// Set up gradable roles.set_config('gradebookroles', $studentrole->id . ',' . $gradeblerole);// Create users.// These will be gradable users.$student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);$student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);$student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);$student5 = $this->getDataGenerator()->create_user(['username' => 'student5']);// These will be non-gradable users.$student4 = $this->getDataGenerator()->create_user(['username' => 'student4']);$student6 = $this->getDataGenerator()->create_user(['username' => 'student6']);$teacher = $this->getDataGenerator()->create_user(['username' => 'teacher']);// Enrol students.$this->getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id);$this->getDataGenerator()->enrol_user($student2->id, $course1->id, $studentrole->id);$this->getDataGenerator()->enrol_user($student3->id, $course1->id, $gradeblerole);$this->getDataGenerator()->enrol_user($student5->id, $course1->id, $nongradeblerole);$this->getDataGenerator()->enrol_user($student6->id, $course1->id, $studentrole->id);$this->getDataGenerator()->enrol_user($teacher->id, $course1->id, $teacherrole->id);// User that is enrolled in a different course.$this->getDataGenerator()->enrol_user($student4->id, $course2->id, $studentrole->id);// Mark user as deleted.$student6->deleted = 1;$DB->update_record('user', $student6);// Create grade items in course 1.$assign1 = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id]);$assign2 = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id]);$quiz1 = $this->getDataGenerator()->create_module('quiz', ['course' => $course1->id]);$manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item(['itemname' => 'Grade item1','idnumber' => 'git1','courseid' => $course1->id,]));// Create grade items in course 2.$assign3 = $this->getDataGenerator()->create_module('assign', ['course' => $course2->id]);// Grade users in first course.$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign1->id));$assigninstance = new assign($cm->context, $cm, $course1);$grade = $assigninstance->get_user_grade($student1->id, true);$grade->grade = 40;$assigninstance->update_grade($grade);$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign2->id));$assigninstance = new assign($cm->context, $cm, $course1);$grade = $assigninstance->get_user_grade($student3->id, true);$grade->grade = 50;$assigninstance->update_grade($grade);// Override grade for assignment in gradebook.$gi = \grade_item::fetch(['itemtype' => 'mod','itemmodule' => 'assign','iteminstance' => $cm->instance,'courseid' => $course1->id]);$gi->update_final_grade($student3->id, 55);// Grade user in second course.$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign3->id));$assigninstance = new assign($cm->context, $cm, $course2);$grade = $assigninstance->get_user_grade($student4->id, true);$grade->grade = 40;$assigninstance->update_grade($grade);$manuaitem->update_final_grade($student1->id, 1);$manuaitem->update_final_grade($student3->id, 2);// Trigger a regrade.grade_force_full_regrading($course1->id);grade_force_full_regrading($course2->id);grade_regrade_final_grades($course1->id);grade_regrade_final_grades($course2->id);// Initialise reports.$context1 = \context_course::instance($course1->id);$context2 = \context_course::instance($course2->id);$gpr1 = new grade_plugin_return(['type' => 'report','plugin' => 'grader','course' => $course1,]);$gpr2 = new grade_plugin_return(['type' => 'report','plugin' => 'grader','course' => $course2,]);$report1 = new grade_report_grader($course1->id, $gpr1, $context1);$report2 = new grade_report_grader($course2->id, $gpr2, $context2);$ungradedcounts = [];$ungradedcounts[$course1->id] = $report1->ungraded_counts(false);$ungradedcounts[$course2->id] = $report2->ungraded_counts(false);foreach ($ungradedcounts as $key => $ungradedcount) {$gradeitems = grade_item::fetch_all(['courseid' => $key]);if ($key == $course1->id) {$gradeitemkeys = array_keys($gradeitems);$ungradedcountskeys = array_keys($ungradedcount['ungradedcounts']);// For each grade item there is some student that is not graded yet in course 1.$this->assertEmpty(array_diff_key($gradeitemkeys, $ungradedcountskeys));// Only quiz does not have any grades, the remaning 4 grade items should have some.// We can do more and match by gradeitem id numbers. But feels like overengeneering.$this->assertEquals(4, count($ungradedcount['sumarray']));} else {// In course 2 there is one student, and he is graded.$this->assertEmpty($ungradedcount['ungradedcounts']);// There are 2 grade items and they both have some grades.$this->assertEquals(2, count($ungradedcount['sumarray']));}foreach ($gradeitems as $gradeitem) {$sumgrades = null;if (array_key_exists($gradeitem->id, $ungradedcount['ungradedcounts'])) {$ungradeditem = $ungradedcount['ungradedcounts'][$gradeitem->id];if ($gradeitem->itemtype === 'course') {$this->assertEquals(1, $ungradeditem->count);} else if ($gradeitem->itemmodule === 'assign') {$this->assertEquals(2, $ungradeditem->count);} else if ($gradeitem->itemmodule === 'quiz') {$this->assertEquals(3, $ungradeditem->count);} else if ($gradeitem->itemtype === 'manual') {$this->assertEquals(1, $ungradeditem->count);}}if (array_key_exists($gradeitem->id, $ungradedcount['sumarray'])) {$sumgrades = $ungradedcount['sumarray'][$gradeitem->id];if ($gradeitem->itemtype === 'course') {if ($key == $course1->id) {$this->assertEquals('98.00000', $sumgrades); // 40 + 55 + 1 + 2} else {$this->assertEquals('40.00000', $sumgrades);}} else if ($gradeitem->itemmodule === 'assign') {if (($gradeitem->itemname === $assign1->name) || ($gradeitem->itemname === $assign3->name)) {$this->assertEquals('40.00000', $sumgrades);} else {$this->assertEquals('55.00000', $sumgrades);}} else if ($gradeitem->itemtype === 'manual') {$this->assertEquals('3.00000', $sumgrades);}}}}}/*** Tests that ungraded_counts calculates count and sum of grades correctly when there are hidden grades.* @dataProvider ungraded_counts_hidden_grades_data()* @param bool $hidden Whether to inlcude hidden grades or not.* @param array $expectedcount expected count value (i.e. number of ugraded grades)* @param array $expectedsumarray expceted sum of grades** @covers \grade_report::ungraded_counts*/public function test_ungraded_counts_hidden_grades(bool $hidden, array $expectedcount, array $expectedsumarray): void {$this->resetAfterTest();$course = $this->getDataGenerator()->create_course();// Create users.$student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);$student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);$student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);// Enrol students.$this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student');$this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student');$this->getDataGenerator()->enrol_user($student3->id, $course->id, 'student');// Create grade items in course.$manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item(['itemname' => 'Grade item1','idnumber' => 'git1','courseid' => $course->id,]));// Grade users.$manuaitem->update_final_grade($student1->id, 1);$manuaitem->update_final_grade($student3->id, 2);// Create a hidden grade.$manuaitem->update_final_grade($student2->id, 3);$grade = \grade_grade::fetch(['itemid' => $manuaitem->id, 'userid' => $student2->id]);$grade->hidden = 1;$grade->update();// Trigger a regrade.grade_force_full_regrading($course->id);grade_regrade_final_grades($course->id);// Initialise reports.$context = \context_course::instance($course->id);$gpr = new grade_plugin_return(['type' => 'report','plugin' => 'grader','course' => $course,]);$report = new grade_report_grader($course->id, $gpr, $context);$ungradedcounts = $report->ungraded_counts(false, $hidden);$gradeitems = grade_item::fetch_all(['courseid' => $course->id]);foreach ($gradeitems as $gradeitem) {$sumgrades = null;if (array_key_exists($gradeitem->id, $ungradedcounts['ungradedcounts'])) {$ungradeditem = $ungradedcounts['ungradedcounts'][$gradeitem->id];if ($gradeitem->itemtype === 'course') {$this->assertEquals($expectedcount['course'], $ungradeditem->count);} else if ($gradeitem->itemtype === 'manual') {$this->assertEquals($expectedcount['Grade item1'], $ungradeditem->count);}}if (array_key_exists($gradeitem->id, $ungradedcounts['sumarray'])) {$sumgrades = $ungradedcounts['sumarray'][$gradeitem->id];if ($gradeitem->itemtype === 'course') {$this->assertEquals($expectedsumarray['course'], $sumgrades);} else if ($gradeitem->itemtype === 'manual') {$this->assertEquals($expectedsumarray['Grade item1'], $sumgrades);}}}}/*** Data provider for test_ungraded_counts_hidden_grades** @return array of testing scenarios*/public function ungraded_counts_hidden_grades_data(): array {return ['nohidden' => ['hidden' => false,'count' => ['course' => 1, 'Grade item1' => 1],'sumarray' => ['course' => 6.00000, 'Grade item1' => 3.00000],],'includehidden' => ['hidden' => true,'count' => ['course' => 1, 'Grade item1' => 2],'sumarray' => ['course' => 6.00000, 'Grade item1' => 6.00000],],];}/*** Tests that ungraded_counts calculates count and sum of grades correctly for groups when there are graded users.** @covers \grade_report::ungraded_counts*/public function test_ungraded_count_sumgrades_groups(): void {global $DB;$this->resetAfterTest();$course = $this->getDataGenerator()->create_course();$studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);// Create users.$student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);$student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);$student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);// Enrol students.$this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);$this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);$this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id);$group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);$group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);$this->getDataGenerator()->create_group_member(['userid' => $student1->id, 'groupid' => $group1->id]);$this->getDataGenerator()->create_group_member(['userid' => $student2->id, 'groupid' => $group2->id]);$this->getDataGenerator()->create_group_member(['userid' => $student3->id, 'groupid' => $group2->id]);$DB->set_field('course', 'groupmode', SEPARATEGROUPS, ['id' => $course->id]);// Create grade items.$assign1 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);$assign2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);$quiz1 = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);$manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item(['itemname' => 'Grade item1','idnumber' => 'git1','courseid' => $course->id,]));// Grade users.$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign1->id));$assigninstance = new assign($cm->context, $cm, $course);$grade = $assigninstance->get_user_grade($student1->id, true);$grade->grade = 40;$assigninstance->update_grade($grade);$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign2->id));$assigninstance = new assign($cm->context, $cm, $course);$grade = $assigninstance->get_user_grade($student3->id, true);$grade->grade = 50;$assigninstance->update_grade($grade);$manuaitem->update_final_grade($student1->id, 1);$manuaitem->update_final_grade($student3->id, 2);// Trigger a regrade.grade_force_full_regrading($course->id);grade_regrade_final_grades($course->id);// Initialise report.$context = \context_course::instance($course->id);$gpr1 = new grade_plugin_return(['type' => 'report','plugin' => 'grader','course' => $course,'groupid' => $group1->id,]);$gpr2 = new grade_plugin_return(['type' => 'report','plugin' => 'grader','course' => $course,'groupid' => $group2->id,]);$report1 = new grade_report_grader($course->id, $gpr1, $context);$report2 = new grade_report_grader($course->id, $gpr2, $context);$ungradedcounts = [];$ungradedcounts[$group1->id] = $report1->ungraded_counts(true);$ungradedcounts[$group2->id] = $report2->ungraded_counts(true);$gradeitems = grade_item::fetch_all(['courseid' => $course->id]);// In group1 there is 1 student and assign1 and quiz1 are not graded for him.$this->assertEquals(2, count($ungradedcounts[$group1->id]['ungradedcounts']));// In group1 manual grade item, assign1 and course total have some grades.$this->assertEquals(3, count($ungradedcounts[$group1->id]['sumarray']));// In group2 student2 has no grades at all so all 5 grade items should present.$this->assertEquals(5, count($ungradedcounts[$group2->id]['ungradedcounts']));// In group2 manual grade item, assign2 and course total have some grades.$this->assertEquals(3, count($ungradedcounts[$group2->id]['sumarray']));foreach ($gradeitems as $gradeitem) {$sumgrades = null;foreach ($ungradedcounts as $key => $ungradedcount) {if (array_key_exists($gradeitem->id, $ungradedcount['ungradedcounts'])) {$ungradeditem = $ungradedcount['ungradedcounts'][$gradeitem->id];if ($key == $group1->id) {// Both assign2 and quiz1 are not graded for student1.$this->assertEquals(1, $ungradeditem->count);} else {if ($gradeitem->itemtype === 'course') {$this->assertEquals(1, $ungradeditem->count);} else if ($gradeitem->itemmodule === 'assign') {if ($gradeitem->itemname === $assign1->name) {// In group2 assign1 is not graded for anyone.$this->assertEquals(2, $ungradeditem->count);} else {// In group2 assign2 is graded for student3.$this->assertEquals(1, $ungradeditem->count);}} else if ($gradeitem->itemmodule === 'quiz') {$this->assertEquals(2, $ungradeditem->count);} else if ($gradeitem->itemtype === 'manual') {$this->assertEquals(1, $ungradeditem->count);}}}if (array_key_exists($gradeitem->id, $ungradedcount['sumarray'])) {$sumgrades = $ungradedcount['sumarray'][$gradeitem->id];if ($key == $group1->id) {if ($gradeitem->itemtype === 'course') {$this->assertEquals('41.00000', $sumgrades);} else if ($gradeitem->itemmodule === 'assign') {$this->assertEquals('40.00000', $sumgrades);} else if ($gradeitem->itemtype === 'manual') {$this->assertEquals('1.00000', $sumgrades);}} else {if ($gradeitem->itemtype === 'course') {$this->assertEquals('52.00000', $sumgrades);} else if ($gradeitem->itemmodule === 'assign') {$this->assertEquals('50.00000', $sumgrades);} else if ($gradeitem->itemtype === 'manual') {$this->assertEquals('2.00000', $sumgrades);}}}}}}/*** Tests that ungraded_counts calculates count and sum of grades correctly when there are hidden grades.* @dataProvider ungraded_counts_only_active_enrol_data()* @param bool $onlyactive Site setting to show only active users.* @param int $hascapability Capability constant* @param bool|null $showonlyactiveenrolpref Show only active user preference.* @param array $expectedcount expected count value (i.e. number of ugraded grades)* @param array $expectedsumarray expected sum of grades** @covers \grade_report::ungraded_counts*/public function test_ungraded_counts_only_active_enrol(bool $onlyactive,int $hascapability, ?bool $showonlyactiveenrolpref, array $expectedcount, array $expectedsumarray): void {global $CFG, $DB;$this->resetAfterTest();$CFG->grade_report_showonlyactiveenrol = $onlyactive;$course = $this->getDataGenerator()->create_course();// Create users.$student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);$student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);$student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');// Enrol students.$this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student');$this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student');// Give teacher 'viewsuspendedusers' capability and set a preference to display suspended users.$roleteacher = $DB->get_record('role', ['shortname' => 'teacher'], '*', MUST_EXIST);$coursecontext = \context_course::instance($course->id);assign_capability('moodle/course:viewsuspendedusers', $hascapability, $roleteacher->id, $coursecontext, true);set_user_preference('grade_report_showonlyactiveenrol', $showonlyactiveenrolpref, $teacher);accesslib_clear_all_caches_for_unit_testing();$this->setUser($teacher);// Suspended student.$this->getDataGenerator()->enrol_user($student3->id, $course->id, 'student', 'manual', 0, 0, ENROL_USER_SUSPENDED);// Create grade items in course.$manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item(['itemname' => 'Grade item1','idnumber' => 'git1','courseid' => $course->id,]));// Grade users.$manuaitem->update_final_grade($student1->id, 1);$manuaitem->update_final_grade($student3->id, 2);// Trigger a regrade.grade_force_full_regrading($course->id);grade_regrade_final_grades($course->id);// Initialise reports.$context = \context_course::instance($course->id);$gpr = new grade_plugin_return(['type' => 'report','plugin' => 'grader','course' => $course,]);$report = new grade_report_grader($course->id, $gpr, $context);$showonlyactiveenrol = $report->show_only_active();$ungradedcounts = $report->ungraded_counts(false, false, $showonlyactiveenrol);$gradeitems = grade_item::fetch_all(['courseid' => $course->id]);foreach ($gradeitems as $gradeitem) {$sumgrades = null;if (array_key_exists($gradeitem->id, $ungradedcounts['ungradedcounts'])) {$ungradeditem = $ungradedcounts['ungradedcounts'][$gradeitem->id];if ($gradeitem->itemtype === 'course') {$this->assertEquals($expectedcount['course'], $ungradeditem->count);} else if ($gradeitem->itemtype === 'manual') {$this->assertEquals($expectedcount['Grade item1'], $ungradeditem->count);}}if (array_key_exists($gradeitem->id, $ungradedcounts['sumarray'])) {$sumgrades = $ungradedcounts['sumarray'][$gradeitem->id];if ($gradeitem->itemtype === 'course') {$this->assertEquals($expectedsumarray['course'], $sumgrades);} else if ($gradeitem->itemtype === 'manual') {$this->assertEquals($expectedsumarray['Grade item1'], $sumgrades);}}}}/*** Data provider for test_ungraded_counts_hidden_grades** @return array of testing scenarios*/public function ungraded_counts_only_active_enrol_data(): array {return ['Show only active and no user preference' => ['onlyactive' => true,'hascapability' => 1,'showonlyactiveenrolpref' => null,'count' => ['course' => 1, 'Grade item1' => 1],'sumarray' => ['course' => 1, 'Grade item1' => 1.00000],],'Show only active and user preference set to true' => ['onlyactive' => true,'hascapability' => 1,'showonlyactiveenrolpref' => true,'count' => ['course' => 1, 'Grade item1' => 1],'sumarray' => ['course' => 1, 'Grade item1' => 1.00000],],'Show only active and user preference set to false' => ['onlyactive' => true,'hascapability' => 1,'showonlyactiveenrolpref' => false,'count' => ['course' => 1, 'Grade item1' => 1],'sumarray' => ['course' => 3.00000, 'Grade item1' => 3.00000],],'Include suspended with capability and user preference set to true' => ['onlyactive' => false,'hascapability' => 1,'showonlyactiveenrolpref' => true,'count' => ['course' => 1, 'Grade item1' => 1],'sumarray' => ['course' => 1.00000, 'Grade item1' => 1.00000],],'Include suspended with capability and user preference set to false' => ['onlyactive' => false,'hascapability' => 1,'showonlyactiveenrolpref' => false,'count' => ['course' => 1, 'Grade item1' => 1],'sumarray' => ['course' => 3.00000, 'Grade item1' => 3.00000],],'Include suspended with capability and no user preference' => ['onlyactive' => false,'hascapability' => 1,'showonlyactiveenrolpref' => null,'count' => ['course' => 1, 'Grade item1' => 1],'sumarray' => ['course' => 3.00000, 'Grade item1' => 3.00000],],'Include suspended without capability' => ['onlyactive' => false,'hascapability' => -1,'showonlyactiveenrolpref' => null,'count' => ['course' => 1, 'Grade item1' => 1],'sumarray' => ['course' => 1.00000, 'Grade item1' => 1.00000],],];}/*** Tests for calculate_average.* @dataProvider calculate_average_data()* @param int $meanselection Whether to inlcude all grades or non-empty grades in aggregation.* @param array $expectedmeancount expected meancount value* @param array $expectedaverage expceted average value** @covers \grade_report::calculate_average*/public function test_calculate_average(int $meanselection, array $expectedmeancount, array $expectedaverage): void {global $DB;$this->resetAfterTest(true);$course = $this->getDataGenerator()->create_course();$student1 = $this->getDataGenerator()->create_user(['username' => 'student1']);$student2 = $this->getDataGenerator()->create_user(['username' => 'student2']);$student3 = $this->getDataGenerator()->create_user(['username' => 'student3']);$studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);// Enrol students.$this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);$this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);$this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id);// Create activities.$assign1 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);$assign2 = $this->getDataGenerator()->create_module('assign', ['course' => $course->id]);$quiz1 = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);// Grade users.$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign1->id));$assigninstance = new assign($cm->context, $cm, $course);$grade = $assigninstance->get_user_grade($student1->id, true);$grade->grade = 40;$assigninstance->update_grade($grade);$grade = $assigninstance->get_user_grade($student2->id, true);$grade->grade = 30;$assigninstance->update_grade($grade);$cm = cm_info::create(get_coursemodule_from_instance('assign', $assign2->id));$assigninstance = new assign($cm->context, $cm, $course);$grade = $assigninstance->get_user_grade($student3->id, true);$grade->grade = 50;$assigninstance->update_grade($grade);$grade = $assigninstance->get_user_grade($student1->id, true);$grade->grade = 100;$assigninstance->update_grade($grade);// Make a manual grade items.$manuaitem = new \grade_item($this->getDataGenerator()->create_grade_item(['itemname' => 'Grade item1','idnumber' => 'git1','courseid' => $course->id,]));$manuaitem->update_final_grade($student1->id, 1);$manuaitem->update_final_grade($student3->id, 2);// Initialise report.$context = \context_course::instance($course->id);$gpr = new grade_plugin_return(['type' => 'report','plugin' => 'grader','course' => $course,]);$report = new grade_report_grader($course->id, $gpr, $context);$ungradedcounts = $report->ungraded_counts(false);$ungradedcounts['report']['meanselection'] = $meanselection;$gradeitems = grade_item::fetch_all(['courseid' => $course->id]);foreach ($gradeitems as $gradeitem) {$name = $gradeitem->itemname . ' ' . $gradeitem->itemtype;$aggr = $report->calculate_average($gradeitem, $ungradedcounts);$this->assertEquals($expectedmeancount[$name], $aggr['meancount']);$this->assertEquals($expectedaverage[$name], $aggr['average']);}}/*** Data provider for test_calculate_average** @return array of testing scenarios*/public function calculate_average_data(): array {return ['Non-empty grades' => ['meanselection' => 1,'expectedmeancount' => [' course' => 3, 'Assignment 1 mod' => 2, 'Assignment 2 mod' => 2,'Quiz 1 mod' => 0, 'Grade item1 manual' => 2],'expectedaverage' => [' course' => 73.33333333333333, 'Assignment 1 mod' => 35.0,'Assignment 2 mod' => 75.0, 'Quiz 1 mod' => null, 'Grade item1 manual' => 1.5],],'All grades' => ['meanselection' => 0,'expectedmeancount' => [' course' => 3, 'Assignment 1 mod' => 3, 'Assignment 2 mod' => 3,'Quiz 1 mod' => 3, 'Grade item1 manual' => 3],'expectedaverage' => [' course' => 73.33333333333333, 'Assignment 1 mod' => 23.333333333333332,'Assignment 2 mod' => 50.0, 'Quiz 1 mod' => null, 'Grade item1 manual' => 1.0],],];}/*** Tests for item types.** @covers \grade_report::item_types*/public function test_item_types(): void {$this->resetAfterTest(true);$course1 = $this->getDataGenerator()->create_course();$course2 = $this->getDataGenerator()->create_course();// Create activities.$this->getDataGenerator()->create_module('assign', ['course' => $course1->id]);$this->getDataGenerator()->create_module('assign', ['course' => $course1->id]);$this->getDataGenerator()->create_module('quiz', ['course' => $course1->id]);$this->getDataGenerator()->create_module('assign', ['course' => $course2->id]);// Create manual grade items.new \grade_item($this->getDataGenerator()->create_grade_item(['itemname' => 'Grade item1','idnumber' => 'git1','courseid' => $course1->id,]));new \grade_item($this->getDataGenerator()->create_grade_item(['itemname' => 'Grade item2','idnumber' => 'git2','courseid' => $course2->id,]));// Create a grade category (it should not be fetched by item_types).new \grade_category($this->getDataGenerator()->create_grade_category(['courseid' => $course1->id]), false);// Initialise reports.$context = \context_course::instance($course1->id);$gpr = new grade_plugin_return(['type' => 'report','plugin' => 'grader','course' => $course1,]);$report1 = new grade_report_grader($course1->id, $gpr, $context);$context = \context_course::instance($course2->id);$gpr = new grade_plugin_return(['type' => 'report','plugin' => 'grader','course' => $course2,]);$report2 = new grade_report_grader($course2->id, $gpr, $context);$gradeitems1 = $report1->item_types();$gradeitems2 = $report2->item_types();$this->assertEquals(3, count($gradeitems1));$this->assertEquals(2, count($gradeitems2));$this->assertArrayHasKey('assign', $gradeitems1);$this->assertArrayHasKey('quiz', $gradeitems1);$this->assertArrayHasKey('manual', $gradeitems1);$this->assertArrayHasKey('assign', $gradeitems2);$this->assertArrayHasKey('manual', $gradeitems2);}/*** Test get_gradable_users() function.** @covers ::get_gradable_users*/public function test_get_gradable_users(): void {global $DB;$this->setAdminUser();$this->resetAfterTest(true);$roleteacher = $DB->get_record('role', ['shortname' => 'teacher'], '*', MUST_EXIST);// Create a course.$course = $this->getDataGenerator()->create_course();$coursecontext = \context_course::instance($course->id);// Create groups.$group1 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);$group2 = $this->getDataGenerator()->create_group(['courseid' => $course->id]);// Create and enrol a teacher and some students into the course.$teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');$student1 = $this->getDataGenerator()->create_and_enrol($course, 'student');$student2 = $this->getDataGenerator()->create_and_enrol($course, 'student');$student3 = $this->getDataGenerator()->create_and_enrol($course, 'student');// Add student1 and student2 to group1.$this->getDataGenerator()->create_group_member(['groupid' => $group1->id, 'userid' => $student1->id]);$this->getDataGenerator()->create_group_member(['groupid' => $group1->id, 'userid' => $student2->id]);// Add student3 to group2.$this->getDataGenerator()->create_group_member(['groupid' => $group2->id, 'userid' => $student3->id]);// Perform a regrade before creating the report.grade_regrade_final_grades($course->id);// Should return all gradable users (only students).$gradableusers = get_gradable_users($course->id);$this->assertEqualsCanonicalizing([$student1->id, $student2->id, $student3->id], array_keys($gradableusers));// Now, let's suspend the enrolment of student2.$this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student', 'manual', 0, 0, ENROL_USER_SUSPENDED);// Should return only the active gradable users (student1 and student3).$gradableusers = \grade_report::get_gradable_users($course->id);$this->assertEqualsCanonicalizing([$student1->id, $student3->id], array_keys($gradableusers));// Give teacher 'viewsuspendedusers' capability and set a preference to display suspended users.assign_capability('moodle/course:viewsuspendedusers', CAP_ALLOW, $roleteacher->id, $coursecontext, true);set_user_preference('grade_report_showonlyactiveenrol', false, $teacher);accesslib_clear_all_caches_for_unit_testing();$this->setUser($teacher);// Should return all gradable users (including suspended enrolments).$gradableusers = \grade_report::get_gradable_users($course->id);$this->assertEqualsCanonicalizing([$student1->id, $student2->id, $student3->id], array_keys($gradableusers));// Reactivate the course enrolment of student2.$this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student', 'manual', 0, 0, ENROL_USER_ACTIVE);$this->setAdminUser();// Should return all gradable users from group1 (student1 and student2).$gradableusers = \grade_report::get_gradable_users($course->id, $group1->id);$this->assertEqualsCanonicalizing([$student1->id, $student2->id], array_keys($gradableusers));// Should return all gradable users from group2 (student3).$gradableusers = \grade_report::get_gradable_users($course->id, $group2->id);$this->assertEqualsCanonicalizing([$student3->id], array_keys($gradableusers));}}