░▒▓████████████████████████████████▓▒░ ░▒▓█ ▓▒░ ░▒▓█ ~ S W A M P ~ ▓▒░ ░▒▓█ ▓▒░ ░▒▓████████████████████████████████▓▒░
pplmvsvm
LOCATION:
/var/www/moodle/public/mod/quiz/tests/task
☗ ROOT
↻ REFRESH
✎ CARVE FLESH
EDITING: precreate_attempts_test.php
<?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/>. namespace mod_quiz\task; use mod_quiz\quiz_attempt; use mod_quiz\quiz_settings; defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->dirroot . '/mod/quiz/tests/quiz_question_helper_test_trait.php'); /** * Unit tests for precreate_attempts * * @package mod_quiz * @copyright 2024 onwards Catalyst IT EU {@link https://catalyst-eu.net} * @author Mark Johnson <mark.johnson@catalyst-eu.net> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @covers \mod_quiz\task\precreate_attempts */ final class precreate_attempts_test extends \advanced_testcase { use \quiz_question_helper_test_trait; /** * Generate the various possible combinations precreation settings and the corresponding task output. * * @return array */ public static function precreate_settings_provider(): array { return [ [ 'period' => 0, 'output' => 'Pre-creation of quiz attempts is disabled. Nothing to do.', ], [ 'period' => 1, 'output' => 'Found 0 quizzes to create attempts for.', ], ]; } /** * The scheduled task only looks for quizzes to generate if pre-creation is configured. * * Only look for quizzes to generate attempts for if precreateperiod is not 0, and precreateattempts is 1 or is unlocked. * * @param int $period * @param string $output * @dataProvider precreate_settings_provider */ public function test_execute_disabled(int $period, string $output): void { $this->resetAfterTest(); set_config('precreateperiod', $period, 'quiz'); $task = new precreate_attempts(); ob_start(); $task->execute(); $log = ob_get_clean(); $this->assertMatchesRegularExpression("/{$output}/", $log); } /** * Test precreate attempts task. * * Generate quizzes with a variety of timeopen and precreateperiod settings, and ensure those that match the criteria * for attempt pre-generation are picked up by the scheduled task. */ public function test_execute(): void { $this->resetAfterTest(); // Generate a course. $course = $this->getDataGenerator()->create_course(); // Generate 3 users. $student1 = $this->getDataGenerator()->create_user(); $student2 = $this->getDataGenerator()->create_user(); $teacher = $this->getDataGenerator()->create_user(); // Enrol users on the course with the appropriate roles. $this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher'); set_config('precreateperiod', 12 * HOURSECS, 'quiz'); set_config('precreateattempts', 1, 'quiz'); $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); // Generate a quiz with timeopen 1 day in the future. $quizinfuture = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() + 86400, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // Generate a quiz with timeopen 0. $quiznotimeopen = $quizgenerator->create_instance([ 'course' => $course->id, 'precreateperiod' => 43200, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // Generate a quiz with timeopen in the past. $quizinpast = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() - 86400, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // Generate a quiz with timeopen 11 hours in the future. $quizwithattempts = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // Generate second quiz with timeopen 11 hours in the future. $quizinprecreateperiod = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // Generate second quiz with timeopen 11 hours in the future, // but do not give it any questions. $quizwithoutquestions = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // Generate a quiz with teacher's preview and timeopen 11 hours in the future. $quizwithpreviewonly = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // Generate a quiz with student attempt, teacher's preview and timeopen 11 hours in the future. $quizwithpreviewandattempt = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // Add questions to the quizzes. $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $this->add_two_regular_questions($questiongenerator, $quizinfuture); $this->add_two_regular_questions($questiongenerator, $quiznotimeopen); $this->add_two_regular_questions($questiongenerator, $quizinpast); $this->add_two_regular_questions($questiongenerator, $quizwithattempts); $this->add_two_regular_questions($questiongenerator, $quizinprecreateperiod); $this->add_two_regular_questions($questiongenerator, $quizwithpreviewonly); $this->add_two_regular_questions($questiongenerator, $quizwithpreviewandattempt); // Create attempts for one student on quiz 5. $this->add_quiz_attempt($quizwithattempts->id, $student1->id, false); // Create attempts for a teacher. $this->add_quiz_attempt($quizwithpreviewonly->id, $teacher->id, true); // Create attempts for a teacher in another quiz. $this->add_quiz_attempt($quizwithpreviewandattempt->id, $teacher->id, true); // Create attempts for student2. $this->add_quiz_attempt($quizwithpreviewandattempt->id, $student2->id, false); $this->assertEmpty( quiz_get_user_attempts( [ $quizinfuture->id, $quiznotimeopen->id, $quizinpast->id, $quizwithoutquestions->id, ], $student1->id, 'all' ) ); $this->assertEmpty( quiz_get_user_attempts( [ $quizinfuture->id, $quiznotimeopen->id, $quizinpast->id, $quizwithattempts->id, $quizwithoutquestions->id, ], $student2->id, 'all', ), ); $this->assertEmpty( quiz_get_user_attempts( [ $quizinfuture->id, $quiznotimeopen->id, $quizinpast->id, $quizwithattempts->id, $quizinprecreateperiod->id, $quizwithoutquestions->id, ], $teacher->id, 'all', ), ); $student1existingattempts = quiz_get_user_attempts($quizwithattempts->id, $student1->id, 'all'); $this->assertCount(1, $student1existingattempts); $this->assertEquals(reset($student1existingattempts)->state, quiz_attempt::IN_PROGRESS); $student1precreatedattempts = quiz_get_user_attempts($quizinprecreateperiod->id, $student1->id, 'all'); $this->assertEmpty($student1precreatedattempts); $student2precreatedattempts = quiz_get_user_attempts($quizinprecreateperiod->id, $student2->id, 'all'); $this->assertEmpty($student2precreatedattempts); $task = new precreate_attempts(); ob_start(); $task->execute(); $log = ob_get_clean(); $this->assertMatchesRegularExpression('/Found 2 quizzes to create attempts for/', $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quizinfuture->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quiznotimeopen->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quizinpast->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quizwithattempts->name}/", $log); $this->assertMatchesRegularExpression("/Creating attempts for {$quizinprecreateperiod->name}/", $log); $this->assertMatchesRegularExpression("/Created 2 attempts for {$quizinprecreateperiod->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quizwithoutquestions->name}/", $log); $this->assertMatchesRegularExpression("/Created 2 attempts for {$quizwithpreviewonly->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quizwithpreviewandattempt->name}/", $log); $this->assertMatchesRegularExpression('/Created attempts for 2 quizzes./', $log); // Students should have no attempts on quizzes that didn't meet criteria for pre-creation. $this->assertEmpty( quiz_get_user_attempts( [ $quizinfuture->id, $quiznotimeopen->id, $quizinpast->id, $quizwithoutquestions->id, ], $student1->id, 'all' ) ); $this->assertEmpty( quiz_get_user_attempts( [ $quizinfuture->id, $quiznotimeopen->id, $quizinpast->id, $quizwithattempts->id, $quizwithoutquestions->id, ], $student2->id, 'all', ), ); // Teacher should not have any attempts on any quizzes. $this->assertEmpty( quiz_get_user_attempts( [ $quizinfuture->id, $quiznotimeopen->id, $quizinpast->id, $quizwithattempts->id, $quizinprecreateperiod->id, $quizwithoutquestions->id, ], $teacher->id, 'all', ), ); // Students existing attempts should remain. $student1existingattempts = quiz_get_user_attempts($quizwithattempts->id, $student1->id, 'all'); $this->assertCount(1, $student1existingattempts); $this->assertEquals(reset($student1existingattempts)->state, quiz_attempt::IN_PROGRESS); // They should have NOT_STARTED attempts on quizzes that meet the criteria for pre-creation. $student1precreatedattempts = quiz_get_user_attempts($quizinprecreateperiod->id, $student1->id, 'all'); $this->assertCount(1, $student1precreatedattempts); $this->assertEquals(reset($student1precreatedattempts)->state, quiz_attempt::NOT_STARTED); $student2precreatedattempts = quiz_get_user_attempts($quizinprecreateperiod->id, $student2->id, 'all'); $this->assertCount(1, $student2precreatedattempts); $this->assertEquals(reset($student1precreatedattempts)->state, quiz_attempt::NOT_STARTED); } /** * Processing should stop at the end of a quiz once maxruntime has been reached. * * @return void */ public function test_execute_maxruntime(): void { $this->resetAfterTest(); // Generate a course. $course = $this->getDataGenerator()->create_course(); // Generate 3 users. $student1 = $this->getDataGenerator()->create_user(); // Enrol users on the course with the appropriate roles. $this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student'); set_config('precreateperiod', 12 * HOURSECS, 'quiz'); set_config('precreateattempts', 1, 'quiz'); $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); // Generate 3 quizzes within the pre-creation window. $timenow = time(); $quiz1 = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => $timenow + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // This quiz opens first, so should be processed first. $quiz2 = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => $timenow + 39599, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); $quiz3 = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => $timenow + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); // Add questions to the quizzes. $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $this->add_two_regular_questions($questiongenerator, $quiz1); $this->add_two_regular_questions($questiongenerator, $quiz2); $this->add_two_regular_questions($questiongenerator, $quiz3); // Run the task with a maxruntime of 0, so that it should stop after processing the first quiz. $task = new precreate_attempts(0); ob_start(); $task->execute(); $log = ob_get_clean(); // Verify that the task stopped after the quiz opening soonest. $this->assertMatchesRegularExpression('/Found 3 quizzes to create attempts for/', $log); $this->assertMatchesRegularExpression("/Creating attempts for {$quiz2->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quiz1->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quiz3->name}/", $log); $this->assertMatchesRegularExpression("/Created 1 attempts for {$quiz2->name}/", $log); $this->assertMatchesRegularExpression('/Time limit reached./', $log); $this->assertMatchesRegularExpression('/Created attempts for 1 quizzes./', $log); // Run the task again with the default maxruntime. ob_start(); $task = new precreate_attempts(); $task->execute(); $log = ob_get_clean(); // Verify that it picks up the remaining quiz for processing. $this->assertMatchesRegularExpression('/Found 2 quizzes to create attempts for/', $log); $this->assertMatchesRegularExpression("/Creating attempts for {$quiz1->name}/", $log); $this->assertMatchesRegularExpression("/Creating attempts for {$quiz3->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quiz2->name}/", $log); $this->assertMatchesRegularExpression("/Created 1 attempts for {$quiz1->name}/", $log); $this->assertMatchesRegularExpression("/Created 1 attempts for {$quiz3->name}/", $log); $this->assertDoesNotMatchRegularExpression('/Time limit reached./', $log); $this->assertMatchesRegularExpression('/Created attempts for 2 quizzes./', $log); } /** * Pre-creation is opt in based on quiz setting. * * @return void */ public function test_execute_optin(): void { $this->resetAfterTest(); // Generate a course. $course = $this->getDataGenerator()->create_course(); // Generate 3 users. $student1 = $this->getDataGenerator()->create_user(); $student2 = $this->getDataGenerator()->create_user(); $teacher = $this->getDataGenerator()->create_user(); // Enrol users on the course with the appropriate roles. $this->getDataGenerator()->enrol_user($student1->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($student2->id, $course->id, 'student'); $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher'); // Precreation is disabled by default. set_config('precreateperiod', 12 * HOURSECS, 'quiz'); set_config('precreateattempts', 0, 'quiz'); $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); // Generate a quiz with timeopen 11 hours in the future, and precreateattempts set to 1. $quizprecreate = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, 'precreateattempts' => 1, ]); // Generate a quiz with timeopen 11 hours in the future, and precreateattempts set to 0. $quiznoprecreate = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, 'precreateattempts' => 0, ]); // Generate a quiz with timeopen 11 hours in the future, and precreateattempts set to null. $quizprecreatenull = $quizgenerator->create_instance([ 'course' => $course->id, 'timeopen' => time() + 39600, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2, ]); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $this->add_two_regular_questions($questiongenerator, $quizprecreate); $this->add_two_regular_questions($questiongenerator, $quiznoprecreate); $this->add_two_regular_questions($questiongenerator, $quizprecreatenull); // Run the task. ob_start(); $task = new precreate_attempts(); $task->execute(); $log = ob_get_clean(); // Attempts were now created for the opted-in quiz. $this->assertMatchesRegularExpression('/Found 1 quizzes to create attempts for/', $log); $this->assertMatchesRegularExpression("/Creating attempts for {$quizprecreate->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quiznoprecreate->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quizprecreatenull->name}/", $log); $this->assertMatchesRegularExpression('/Created attempts for 1 quizzes./', $log); // Now enabled by default. set_config('precreateattempts', 1, 'quiz'); // Run the task again. ob_start(); $task = new precreate_attempts(); $task->execute(); $log = ob_get_clean(); // The quiz with null now has attempts generated. $this->assertMatchesRegularExpression('/Found 1 quizzes to create attempts for/', $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quizprecreate->name}/", $log); $this->assertDoesNotMatchRegularExpression("/Creating attempts for {$quiznoprecreate->name}/", $log); $this->assertMatchesRegularExpression("/Creating attempts for {$quizprecreatenull->name}/", $log); $this->assertMatchesRegularExpression('/Created attempts for 1 quizzes./', $log); } /** * Helper to add a quiz attempt for a user. * * @param int $quizid * @param int $userid * @param bool $ispreview * @return void */ protected function add_quiz_attempt($quizid, $userid, bool $ispreview) { $quiz5settings = quiz_settings::create($quizid); $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quiz5settings->get_context()); $quba->set_preferred_behaviour($quiz5settings->get_quiz()->preferredbehaviour); $attempt = quiz_create_attempt($quiz5settings, 1, false, time(), $ispreview, $userid); quiz_start_new_attempt($quiz5settings, $quba, $attempt, 1, time()); quiz_attempt_save_started($quiz5settings, $quba, $attempt); } }
CANCEL
Name
Type
Size
Modified
Actions
↩ ..
DIR
—
—
📄 precreate_attempts_test.php
PHP
21.2 KB
2026-02-11 01:44
EDIT