MDL-27570 quiz timing: add unit test and simplify top of process_attempt

This commit is contained in:
Tim Hunt
2022-09-22 11:11:37 +01:00
parent 58f3d8f191
commit dc3e9e1522
2 changed files with 77 additions and 23 deletions
+18 -17
View File
@@ -2386,28 +2386,32 @@ class quiz_attempt {
$transaction = $DB->start_delegated_transaction();
// If there is only a very small amount of time left, there is no point trying
// to show the student another page of the quiz. Just finish now.
$graceperiodmin = null;
// Get key times.
$accessmanager = $this->get_access_manager($timenow);
$timeclose = $accessmanager->get_end_time($this->get_attempt());
$graceperiodmin = get_config('quiz', 'graceperiodmin');
// Don't enforce timeclose for previews.
if ($this->is_preview()) {
$timeclose = false;
}
$toolate = false;
if ($timeclose !== false && $timenow > $timeclose - QUIZ_MIN_TIME_TO_CONTINUE) {
$timeup = true;
$graceperiodmin = get_config('quiz', 'graceperiodmin');
if ($timenow > $timeclose + $graceperiodmin) {
$toolate = true;
}
}
// Verify if time is really up; time limit could have been changed.
if ($timeup && $timenow < $timeclose - QUIZ_MIN_TIME_TO_CONTINUE) {
$timeup = false;
// Check where we are in relation to the end time, if there is one.
$toolate = false;
if ($timeclose !== false) {
if ($timenow > $timeclose - QUIZ_MIN_TIME_TO_CONTINUE) {
// If there is only a very small amount of time left, there is no point trying
// to show the student another page of the quiz. Just finish now.
$timeup = true;
if ($timenow > $timeclose + $graceperiodmin) {
$toolate = true;
}
} else {
// If time is not close to expiring, then ignore the client-side timer's opinion
// about whether time has expired. This can happen if the time limit has changed
// since the student's previous interaction.
$timeup = false;
}
}
// If time is running out, trigger the appropriate action.
@@ -2415,9 +2419,6 @@ class quiz_attempt {
$becomingabandoned = false;
if ($timeup) {
if ($this->get_quiz()->overduehandling === 'graceperiod') {
if (is_null($graceperiodmin)) {
$graceperiodmin = get_config('quiz', 'graceperiodmin');
}
if ($timenow > $timeclose + $this->get_quiz()->graceperiod + $graceperiodmin) {
// Grace period has run out.
$finishattempt = true;
+59 -6
View File
@@ -34,6 +34,7 @@ require_once($CFG->dirroot . '/mod/quiz/locallib.php');
* @copyright 2013 The Open University
* @author Jamie Pratt <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \quiz_attempt
*/
class attempt_walkthrough_test extends \advanced_testcase {
@@ -116,9 +117,17 @@ class attempt_walkthrough_test extends \advanced_testcase {
$this->assertEquals(100, $gradebookgrade->grade);
}
public function test_quiz_attempt_walkthrough_submit_time_recorded_correctly_when_overdue() {
/**
* Create a quiz containing one question and a close time.
*
* The question is the standard shortanswer test question.
* The quiz is set to close 1 hour from now.
* The quiz is set to use a grade period of 1 hour once time expires.
*
* @return \stdClass the quiz that was created.
*/
protected function create_quiz_with_one_question(): \stdClass {
global $SITE;
$this->resetAfterTest();
// Make a quiz.
@@ -130,6 +139,7 @@ class attempt_walkthrough_test extends \advanced_testcase {
'overduehandling' => 'graceperiod', 'graceperiod' => HOURSECS]);
// Create a question.
/** @var \core_question_generator $questiongenerator */
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
$cat = $questiongenerator->create_question_category();
$saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
@@ -138,6 +148,13 @@ class attempt_walkthrough_test extends \advanced_testcase {
quiz_add_quiz_question($saq->id, $quiz, 0, 1);
quiz_update_sumgrades($quiz);
return $quiz;
}
public function test_quiz_attempt_walkthrough_submit_time_recorded_correctly_when_overdue() {
$quiz = $this->create_quiz_with_one_question();
// Make a user to do the quiz.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
@@ -148,11 +165,11 @@ class attempt_walkthrough_test extends \advanced_testcase {
// Process some responses from the student.
$attemptobj = quiz_attempt::create($attempt->id);
$attemptobj->process_submitted_actions($timeclose - 30 * MINSECS, false, [1 => ['answer' => 'frog']]);
$attemptobj->process_submitted_actions($quiz->timeclose - 30 * MINSECS, false, [1 => ['answer' => 'frog']]);
// Attempt goes overdue (e.g. if cron ran).
$attemptobj = quiz_attempt::create($attempt->id);
$attemptobj->process_going_overdue($timeclose + 2 * get_config('quiz', 'graceperiodmin'), false);
$attemptobj->process_going_overdue($quiz->timeclose + 2 * get_config('quiz', 'graceperiodmin'), false);
// Verify the attempt state.
$attemptobj = quiz_attempt::create($attempt->id);
@@ -164,17 +181,53 @@ class attempt_walkthrough_test extends \advanced_testcase {
// Student submits the attempt during the grace period.
$attemptobj = quiz_attempt::create($attempt->id);
$attemptobj->process_attempt($timeclose + 30 * MINSECS, true, false, 1);
$attemptobj->process_attempt($quiz->timeclose + 30 * MINSECS, true, false, 1);
// Verify the attempt state.
$attemptobj = quiz_attempt::create($attempt->id);
$this->assertEquals(1, $attemptobj->get_attempt_number());
$this->assertEquals(true, $attemptobj->is_finished());
$this->assertEquals($timeclose + 30 * MINSECS, $attemptobj->get_submitted_date());
$this->assertEquals($quiz->timeclose + 30 * MINSECS, $attemptobj->get_submitted_date());
$this->assertEquals($user->id, $attemptobj->get_userid());
$this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
}
public function test_quiz_attempt_walkthrough_close_time_extended_at_last_minute() {
global $DB;
$quiz = $this->create_quiz_with_one_question();
$originaltimeclose = $quiz->timeclose;
// Make a user to do the quiz.
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$quizobj = quiz::create($quiz->id, $user->id);
// Start the attempt.
$attempt = quiz_prepare_and_start_new_attempt($quizobj, 1, null);
// Process some responses from the student during the attempt.
$attemptobj = quiz_attempt::create($attempt->id);
$attemptobj->process_submitted_actions($originaltimeclose - 30 * MINSECS, false, [1 => ['answer' => 'frog']]);
// Teacher edits the quiz to extend the time-limit by one minute.
$DB->set_field('quiz', 'timeclose', $originaltimeclose + MINSECS, ['id' => $quiz->id]);
\course_modinfo::clear_instance_cache($quiz->course);
// Timer expires in the student browser and thinks it is time to submit the quiz.
// This sets $finishattempt to false - since the student did not click the button, and $timeup to true.
$attemptobj = quiz_attempt::create($attempt->id);
$attemptobj->process_attempt($originaltimeclose, false, true, 1);
// Verify the attempt state - the $timeup was ignored becuase things have changed server-side.
$attemptobj = quiz_attempt::create($attempt->id);
$this->assertEquals(1, $attemptobj->get_attempt_number());
$this->assertFalse($attemptobj->is_finished());
$this->assertEquals(quiz_attempt::IN_PROGRESS, $attemptobj->get_state());
$this->assertEquals(0, $attemptobj->get_submitted_date());
$this->assertEquals($user->id, $attemptobj->get_userid());
}
/**
* Create a quiz with a random as well as other questions and walk through quiz attempts.
*/