AutorÃa | 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/>./*** Assignment activity-related unit tests for Completion Progress block.** @package block_completion_progress* @copyright 2020 Jonathon Fowler <fowlerj@usq.edu.au>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/namespace block_completion_progress;defined('MOODLE_INTERNAL') || die();global $CFG;require_once($CFG->dirroot.'/mod/assign/locallib.php');require_once($CFG->dirroot.'/mod/assign/tests/fixtures/testable_assign.php');use block_completion_progress\completion_progress;use block_completion_progress\defaults;/*** Assignment activity-related unit tests for Completion Progress block.** @package block_completion_progress* @copyright 2020 Jonathon Fowler <fowlerj@usq.edu.au>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class assign_completion_test extends \block_completion_progress\tests\completion_testcase {/*** Test assignment completion state changes.* @covers \block_completion_progress\completion_progress*/public function test_assign_get_completion_state() {global $DB, $PAGE;$output = $PAGE->get_renderer('block_completion_progress');$generator = $this->getDataGenerator();$instance = $generator->create_module('assign', ['course' => $this->course->id,'submissiondrafts' => 0,'assignsubmission_onlinetext_enabled' => 1,'assignsubmission_file_enabled' => 0,'completionsubmit' => 1,'completionusegrade' => 1,'completionpassgrade' => 0,'completion' => COMPLETION_TRACKING_AUTOMATIC]);$cm = get_coursemodule_from_instance('assign', $instance->id);$context = \context_module::instance($cm->id);$assign = new \assign($context, $cm, $this->course);$completion = new \completion_info($this->course);$student1 = $generator->create_and_enrol($this->course, 'student');$this->setUser($student1);$result = $completion->get_data($assign->get_course_module(), false, $student1->id);$this->assertSame(COMPLETION_INCOMPLETE, $result->customcompletion['completionsubmit'], 'no submission');$this->assertSame(COMPLETION_INCOMPLETE, $result->completionstate, 'no submission');$progress = (new completion_progress($this->course))->for_user($student1)->for_block_instance($this->blockinstance);$text = $output->render($progress);$this->assertStringContainsStringIgnoringCase('assign', $text, 'no submission');$this->assertStringNotContainsStringIgnoringCase('quiz', $text, 'no submission');// Not yet submitted, nor completed.$this->assertStringContainsString('futureNotCompleted', $text, 'no submission');$submission = $assign->get_user_submission($student1->id, true);$data = (object)['userid' => $student1->id,'onlinetext_editor' => ['text' => 'Text', 'format' => FORMAT_PLAIN],];$notices = [];$this->assertTrue($assign->save_submission($data, $notices), 'submitted awaiting grade');$this->assertEmpty($notices, 'submitted awaiting grade');$result = $completion->get_data($assign->get_course_module(), false, $student1->id);$this->assertSame(COMPLETION_COMPLETE, $result->customcompletion['completionsubmit'], 'submitted awaiting grade');$this->assertSame(COMPLETION_INCOMPLETE, $result->completiongrade, 'submitted awaiting grade');$this->assertSame(COMPLETION_INCOMPLETE, $result->completionstate, 'submitted awaiting grade');$progress = (new completion_progress($this->course))->for_user($student1)->for_block_instance($this->blockinstance);$text = $output->render($progress);// Submitted and awaiting a grade, so not completed.$this->assertStringContainsString('submittedNotComplete', $text, 'submitted awaiting grade');}/*** Test completion determination in an Assignment activity with pass/fail enabled.* @covers \block_completion_progress\completion_progress*/public function test_assign_passfail() {$generator = $this->getDataGenerator();$instance = $generator->create_module('assign', ['course' => $this->course->id,'grade' => 100,'maxattempts' => -1,'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS,'completion' => COMPLETION_TRACKING_AUTOMATIC,'completionusegrade' => 1, // The student must receive a grade to complete.'completionexpected' => time() - DAYSECS,'teamsubmission' => 0,]);$cm = get_coursemodule_from_id('assign', $instance->cmid);// Set the passing grade.$item = \grade_item::fetch(['courseid' => $this->course->id, 'itemtype' => 'mod','itemmodule' => 'assign', 'iteminstance' => $instance->id, 'outcomeid' => null]);$item->gradepass = 50;$item->update();$assign = new \mod_assign_testable_assign(\context_module::instance($cm->id), $cm, $this->course);$teacher = $generator->create_and_enrol($this->course, 'editingteacher');// Student 1 submits to the activity and gets graded correct.$student1 = $generator->create_and_enrol($this->course, 'student');$this->assert_progress_completion($student1, $cm, COMPLETION_INCOMPLETE);$this->submit_for_student($student1, $assign);$this->assert_progress_completion($student1, $cm, 'submitted');$this->grade_student($student1, $assign, $teacher, 75, 0); // Pass.$this->assert_progress_completion($student1, $cm, COMPLETION_COMPLETE_PASS);// Student 2 submits to the activity and gets graded incorrect.$student2 = $generator->create_and_enrol($this->course, 'student');$this->assert_progress_completion($student2, $cm, COMPLETION_INCOMPLETE);$this->submit_for_student($student2, $assign);$this->assert_progress_completion($student2, $cm, 'submitted');$this->grade_student($student2, $assign, $teacher, 25, 0); // Fail.$this->assert_progress_completion($student2, $cm, COMPLETION_COMPLETE_FAIL);// Student 2 then submits again.$this->submit_for_student($student2, $assign);$this->assert_progress_completion($student2, $cm, 'submitted');}/*** Test completion determination in an Assignment activity with basic completion.* @covers \block_completion_progress\completion_progress*/public function test_assign_basic() {$generator = $this->getDataGenerator();$instance = $generator->create_module('assign', ['course' => $this->course->id,'maxattempts' => -1,'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS,'completion' => COMPLETION_TRACKING_AUTOMATIC,'completionsubmit' => 1, // Submission alone is enough to trigger completion.'completionexpected' => time() - DAYSECS,'teamsubmission' => 0,]);$cm = get_coursemodule_from_id('assign', $instance->cmid);$assign = new \mod_assign_testable_assign(\context_module::instance($cm->id), $cm, $this->course);$teacher = $generator->create_and_enrol($this->course, 'editingteacher');// Student 1 submits to the activity and gets graded correctly.$student1 = $generator->create_and_enrol($this->course, 'student');$this->assert_progress_completion($student1, $cm, COMPLETION_INCOMPLETE);$this->submit_for_student($student1, $assign);$this->assert_progress_completion($student1, $cm, COMPLETION_COMPLETE);$this->grade_student($student1, $assign, $teacher, 75, 0); // Pass.$this->assert_progress_completion($student1, $cm, COMPLETION_COMPLETE);// Student 2 submits to the activity and gets graded incorrectly.$student2 = $generator->create_and_enrol($this->course, 'student');$this->assert_progress_completion($student2, $cm, COMPLETION_INCOMPLETE);$this->submit_for_student($student2, $assign);$this->assert_progress_completion($student2, $cm, COMPLETION_COMPLETE);$this->grade_student($student2, $assign, $teacher, 25, 0); // Fail.$this->assert_progress_completion($student2, $cm, COMPLETION_COMPLETE);// Student 2 then submits again.$this->submit_for_student($student2, $assign);$this->assert_progress_completion($student2, $cm, COMPLETION_COMPLETE);}/*** A data provider supplying each of the possible quiz grade methods.* @return array*/public function teamsubmission_provider(): array {return ['one-per-group' => [ 0, ],'per-member' => [ 1, ],];}/*** Test completion determination in an Assignment activity requiring team submissions,* one submission per group.** @param integer $requireallteammemberssubmit** @covers \block_completion_progress\completion_progress* @dataProvider teamsubmission_provider*/public function test_teamsubmission($requireallteammemberssubmit) {$generator = $this->getDataGenerator();$grouping1 = $generator->create_grouping(['courseid' => $this->course->id]);$group1 = $generator->create_group(['courseid' => $this->course->id]);$group2 = $generator->create_group(['courseid' => $this->course->id]);$generator->create_grouping_group(['groupingid' => $grouping1->id, 'groupid' => $group1->id]);$generator->create_grouping_group(['groupingid' => $grouping1->id, 'groupid' => $group2->id]);$instance = $generator->create_module('assign', ['course' => $this->course->id,'maxattempts' => -1,'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_NONE,'completion' => COMPLETION_TRACKING_AUTOMATIC,'completionusegrade' => 1, // The student must receive a grade to complete.'completionexpected' => time() - DAYSECS,'teamsubmission' => 1,'teamsubmissiongroupingid' => $grouping1->id,'requireallteammemberssubmit' => $requireallteammemberssubmit,'preventsubmissionnotingroup' => 0,]);$cm = get_coursemodule_from_id('assign', $instance->cmid);$teacher = $generator->create_and_enrol($this->course, 'editingteacher');// Students 1 and 2 are grouped together.$student1 = $generator->create_and_enrol($this->course, 'student');$generator->create_group_member(['groupid' => $group1->id, 'userid' => $student1->id]);$student2 = $generator->create_and_enrol($this->course, 'student');$generator->create_group_member(['groupid' => $group1->id, 'userid' => $student2->id]);// Student 3 is not a group member.$student3 = $generator->create_and_enrol($this->course, 'student');$assign = new \mod_assign_testable_assign(\context_module::instance($cm->id), $cm, $this->course);if ($requireallteammemberssubmit == 0) { // One-per-group.// Student 1 submits for Group 1.$this->assert_progress_completion($student1, $cm, COMPLETION_INCOMPLETE);$this->assert_progress_completion($student2, $cm, COMPLETION_INCOMPLETE);$this->submit_for_student($student1, $assign);$this->assert_progress_completion($student1, $cm, 'submitted');$this->assert_progress_completion($student2, $cm, 'submitted');$this->grade_student($student1, $assign, $teacher, 75, 0); // Pass.$this->grade_student($student2, $assign, $teacher, 25, 0); // Fail.$this->assert_progress_completion($student1, $cm, COMPLETION_COMPLETE);$this->assert_progress_completion($student2, $cm, COMPLETION_COMPLETE);// Student 2 submits for themself ungrouped.$this->assert_progress_completion($student3, $cm, COMPLETION_INCOMPLETE);$this->submit_for_student($student3, $assign);$this->assert_progress_completion($student3, $cm, 'submitted');$this->grade_student($student3, $assign, $teacher, 75, 0); // Pass.$this->assert_progress_completion($student3, $cm, COMPLETION_COMPLETE);} else {// Set the passing grade.$item = \grade_item::fetch(['courseid' => $this->course->id, 'itemtype' => 'mod','itemmodule' => 'assign', 'iteminstance' => $instance->id, 'outcomeid' => null]);$item->gradepass = 50;$item->update();// Students 1 and 2 submit individually for Group 1.$this->assert_progress_completion($student1, $cm, COMPLETION_INCOMPLETE);$this->assert_progress_completion($student2, $cm, COMPLETION_INCOMPLETE);$this->submit_for_student($student1, $assign);$this->assert_progress_completion($student1, $cm, 'submitted');$this->assert_progress_completion($student2, $cm, COMPLETION_INCOMPLETE);$this->submit_for_student($student2, $assign);$this->assert_progress_completion($student2, $cm, 'submitted');$this->grade_student($student1, $assign, $teacher, 75, 0); // Pass.$this->grade_student($student2, $assign, $teacher, 25, 0); // Fail.$this->assert_progress_completion($student1, $cm, COMPLETION_COMPLETE_PASS);$this->assert_progress_completion($student2, $cm, COMPLETION_COMPLETE_FAIL);}}/*** Submit an assignment.* Pinched from mod/assign/tests/generator.php and modified.** @param object $student* @param assign $assign** @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>*/private function submit_for_student($student, $assign) {$this->setUser($student);$sink = $this->redirectMessages();$assign->save_submission((object) ['userid' => $student->id,'onlinetext_editor' => ['itemid' => file_get_unused_draft_itemid(),'text' => 'Text','format' => FORMAT_HTML,]], $notices);$assign->submit_for_grading((object) ['userid' => $student->id,], []);$sink->close();}/*** Award a grade to a submission.* Pinched from mod/assign/tests/generator.php and modified.** @param object $student* @param assign $assign* @param object $teacher* @param integer $grade* @param integer $attempt** @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>*/private function grade_student($student, $assign, $teacher, $grade, $attempt) {global $DB;$this->setUser($teacher);// Bump all timecreated and timemodified for this user back.$DB->execute('UPDATE {assign_submission} ' .'SET timecreated = timecreated - 1, timemodified = timemodified - 1 ' .'WHERE userid = :userid',['userid' => $student->id]);$assign->testable_apply_grade_to_user((object) [ 'grade' => $grade ],$student->id, $attempt);}}