See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 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. 8 // 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. 13 // 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/>. 16 17 /** 18 * Unit tests for (some of) mod/quiz/locallib.php. 19 * 20 * @package mod_quiz 21 * @category test 22 * @copyright 2008 The Open University 23 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 24 */ 25 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 global $CFG; 30 require_once($CFG->dirroot . '/mod/quiz/lib.php'); 31 32 /** 33 * @copyright 2008 The Open University 34 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 35 */ 36 class mod_quiz_lib_testcase extends advanced_testcase { 37 public function test_quiz_has_grades() { 38 $quiz = new stdClass(); 39 $quiz->grade = '100.0000'; 40 $quiz->sumgrades = '100.0000'; 41 $this->assertTrue(quiz_has_grades($quiz)); 42 $quiz->sumgrades = '0.0000'; 43 $this->assertFalse(quiz_has_grades($quiz)); 44 $quiz->grade = '0.0000'; 45 $this->assertFalse(quiz_has_grades($quiz)); 46 $quiz->sumgrades = '100.0000'; 47 $this->assertFalse(quiz_has_grades($quiz)); 48 } 49 50 public function test_quiz_format_grade() { 51 $quiz = new stdClass(); 52 $quiz->decimalpoints = 2; 53 $this->assertEquals(quiz_format_grade($quiz, 0.12345678), format_float(0.12, 2)); 54 $this->assertEquals(quiz_format_grade($quiz, 0), format_float(0, 2)); 55 $this->assertEquals(quiz_format_grade($quiz, 1.000000000000), format_float(1, 2)); 56 $quiz->decimalpoints = 0; 57 $this->assertEquals(quiz_format_grade($quiz, 0.12345678), '0'); 58 } 59 60 public function test_quiz_get_grade_format() { 61 $quiz = new stdClass(); 62 $quiz->decimalpoints = 2; 63 $this->assertEquals(quiz_get_grade_format($quiz), 2); 64 $this->assertEquals($quiz->questiondecimalpoints, -1); 65 $quiz->questiondecimalpoints = 2; 66 $this->assertEquals(quiz_get_grade_format($quiz), 2); 67 $quiz->decimalpoints = 3; 68 $quiz->questiondecimalpoints = -1; 69 $this->assertEquals(quiz_get_grade_format($quiz), 3); 70 $quiz->questiondecimalpoints = 4; 71 $this->assertEquals(quiz_get_grade_format($quiz), 4); 72 } 73 74 public function test_quiz_format_question_grade() { 75 $quiz = new stdClass(); 76 $quiz->decimalpoints = 2; 77 $quiz->questiondecimalpoints = 2; 78 $this->assertEquals(quiz_format_question_grade($quiz, 0.12345678), format_float(0.12, 2)); 79 $this->assertEquals(quiz_format_question_grade($quiz, 0), format_float(0, 2)); 80 $this->assertEquals(quiz_format_question_grade($quiz, 1.000000000000), format_float(1, 2)); 81 $quiz->decimalpoints = 3; 82 $quiz->questiondecimalpoints = -1; 83 $this->assertEquals(quiz_format_question_grade($quiz, 0.12345678), format_float(0.123, 3)); 84 $this->assertEquals(quiz_format_question_grade($quiz, 0), format_float(0, 3)); 85 $this->assertEquals(quiz_format_question_grade($quiz, 1.000000000000), format_float(1, 3)); 86 $quiz->questiondecimalpoints = 4; 87 $this->assertEquals(quiz_format_question_grade($quiz, 0.12345678), format_float(0.1235, 4)); 88 $this->assertEquals(quiz_format_question_grade($quiz, 0), format_float(0, 4)); 89 $this->assertEquals(quiz_format_question_grade($quiz, 1.000000000000), format_float(1, 4)); 90 } 91 92 /** 93 * Test deleting a quiz instance. 94 */ 95 public function test_quiz_delete_instance() { 96 global $SITE, $DB; 97 $this->resetAfterTest(true); 98 $this->setAdminUser(); 99 100 // Setup a quiz with 1 standard and 1 random question. 101 $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); 102 $quiz = $quizgenerator->create_instance(array('course' => $SITE->id, 'questionsperpage' => 3, 'grade' => 100.0)); 103 104 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 105 $cat = $questiongenerator->create_question_category(); 106 $standardq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id)); 107 108 quiz_add_quiz_question($standardq->id, $quiz); 109 quiz_add_random_questions($quiz, 0, $cat->id, 1, false); 110 111 // Get the random question. 112 $randomq = $DB->get_record('question', array('qtype' => 'random')); 113 114 quiz_delete_instance($quiz->id); 115 116 // Check that the random question was deleted. 117 $count = $DB->count_records('question', array('id' => $randomq->id)); 118 $this->assertEquals(0, $count); 119 // Check that the standard question was not deleted. 120 $count = $DB->count_records('question', array('id' => $standardq->id)); 121 $this->assertEquals(1, $count); 122 123 // Check that all the slots were removed. 124 $count = $DB->count_records('quiz_slots', array('quizid' => $quiz->id)); 125 $this->assertEquals(0, $count); 126 127 // Check that the quiz was removed. 128 $count = $DB->count_records('quiz', array('id' => $quiz->id)); 129 $this->assertEquals(0, $count); 130 } 131 132 /** 133 * Test checking the completion state of a quiz. 134 */ 135 public function test_quiz_get_completion_state() { 136 global $CFG, $DB; 137 $this->resetAfterTest(true); 138 139 // Enable completion before creating modules, otherwise the completion data is not written in DB. 140 $CFG->enablecompletion = true; 141 142 // Create a course and student. 143 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => true)); 144 $passstudent = $this->getDataGenerator()->create_user(); 145 $failstudent = $this->getDataGenerator()->create_user(); 146 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 147 $this->assertNotEmpty($studentrole); 148 149 // Enrol students. 150 $this->assertTrue($this->getDataGenerator()->enrol_user($passstudent->id, $course->id, $studentrole->id)); 151 $this->assertTrue($this->getDataGenerator()->enrol_user($failstudent->id, $course->id, $studentrole->id)); 152 153 // Make a scale and an outcome. 154 $scale = $this->getDataGenerator()->create_scale(); 155 $data = array('courseid' => $course->id, 156 'fullname' => 'Team work', 157 'shortname' => 'Team work', 158 'scaleid' => $scale->id); 159 $outcome = $this->getDataGenerator()->create_grade_outcome($data); 160 161 // Make a quiz with the outcome on. 162 $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); 163 $data = array('course' => $course->id, 164 'outcome_'.$outcome->id => 1, 165 'grade' => 100.0, 166 'questionsperpage' => 0, 167 'sumgrades' => 1, 168 'completion' => COMPLETION_TRACKING_AUTOMATIC, 169 'completionusegrade' => 1, 170 'completionpass' => 1); 171 $quiz = $quizgenerator->create_instance($data); 172 $cm = get_coursemodule_from_id('quiz', $quiz->cmid); 173 174 // Create a couple of questions. 175 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 176 177 $cat = $questiongenerator->create_question_category(); 178 $question = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); 179 quiz_add_quiz_question($question->id, $quiz); 180 181 $quizobj = quiz::create($quiz->id, $passstudent->id); 182 183 // Set grade to pass. 184 $item = grade_item::fetch(array('courseid' => $course->id, 'itemtype' => 'mod', 185 'itemmodule' => 'quiz', 'iteminstance' => $quiz->id, 'outcomeid' => null)); 186 $item->gradepass = 80; 187 $item->update(); 188 189 // Start the passing attempt. 190 $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); 191 $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); 192 193 $timenow = time(); 194 $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $passstudent->id); 195 quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow); 196 quiz_attempt_save_started($quizobj, $quba, $attempt); 197 198 // Process some responses from the student. 199 $attemptobj = quiz_attempt::create($attempt->id); 200 $tosubmit = array(1 => array('answer' => '3.14')); 201 $attemptobj->process_submitted_actions($timenow, false, $tosubmit); 202 203 // Finish the attempt. 204 $attemptobj = quiz_attempt::create($attempt->id); 205 $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question()); 206 $attemptobj->process_finish($timenow, false); 207 208 // Start the failing attempt. 209 $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); 210 $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); 211 212 $timenow = time(); 213 $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $failstudent->id); 214 quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow); 215 quiz_attempt_save_started($quizobj, $quba, $attempt); 216 217 // Process some responses from the student. 218 $attemptobj = quiz_attempt::create($attempt->id); 219 $tosubmit = array(1 => array('answer' => '0')); 220 $attemptobj->process_submitted_actions($timenow, false, $tosubmit); 221 222 // Finish the attempt. 223 $attemptobj = quiz_attempt::create($attempt->id); 224 $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question()); 225 $attemptobj->process_finish($timenow, false); 226 227 // Check the results. 228 $this->assertTrue(quiz_get_completion_state($course, $cm, $passstudent->id, 'return')); 229 $this->assertFalse(quiz_get_completion_state($course, $cm, $failstudent->id, 'return')); 230 } 231 232 public function test_quiz_get_user_attempts() { 233 global $DB; 234 $this->resetAfterTest(); 235 236 $dg = $this->getDataGenerator(); 237 $quizgen = $dg->get_plugin_generator('mod_quiz'); 238 $course = $dg->create_course(); 239 $u1 = $dg->create_user(); 240 $u2 = $dg->create_user(); 241 $u3 = $dg->create_user(); 242 $u4 = $dg->create_user(); 243 $role = $DB->get_record('role', ['shortname' => 'student']); 244 245 $dg->enrol_user($u1->id, $course->id, $role->id); 246 $dg->enrol_user($u2->id, $course->id, $role->id); 247 $dg->enrol_user($u3->id, $course->id, $role->id); 248 $dg->enrol_user($u4->id, $course->id, $role->id); 249 250 $quiz1 = $quizgen->create_instance(['course' => $course->id, 'sumgrades' => 2]); 251 $quiz2 = $quizgen->create_instance(['course' => $course->id, 'sumgrades' => 2]); 252 253 // Questions. 254 $questgen = $dg->get_plugin_generator('core_question'); 255 $quizcat = $questgen->create_question_category(); 256 $question = $questgen->create_question('numerical', null, ['category' => $quizcat->id]); 257 quiz_add_quiz_question($question->id, $quiz1); 258 quiz_add_quiz_question($question->id, $quiz2); 259 260 $quizobj1a = quiz::create($quiz1->id, $u1->id); 261 $quizobj1b = quiz::create($quiz1->id, $u2->id); 262 $quizobj1c = quiz::create($quiz1->id, $u3->id); 263 $quizobj1d = quiz::create($quiz1->id, $u4->id); 264 $quizobj2a = quiz::create($quiz2->id, $u1->id); 265 266 // Set attempts. 267 $quba1a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1a->get_context()); 268 $quba1a->set_preferred_behaviour($quizobj1a->get_quiz()->preferredbehaviour); 269 $quba1b = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1b->get_context()); 270 $quba1b->set_preferred_behaviour($quizobj1b->get_quiz()->preferredbehaviour); 271 $quba1c = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1c->get_context()); 272 $quba1c->set_preferred_behaviour($quizobj1c->get_quiz()->preferredbehaviour); 273 $quba1d = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1d->get_context()); 274 $quba1d->set_preferred_behaviour($quizobj1d->get_quiz()->preferredbehaviour); 275 $quba2a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context()); 276 $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour); 277 278 $timenow = time(); 279 280 // User 1 passes quiz 1. 281 $attempt = quiz_create_attempt($quizobj1a, 1, false, $timenow, false, $u1->id); 282 quiz_start_new_attempt($quizobj1a, $quba1a, $attempt, 1, $timenow); 283 quiz_attempt_save_started($quizobj1a, $quba1a, $attempt); 284 $attemptobj = quiz_attempt::create($attempt->id); 285 $attemptobj->process_submitted_actions($timenow, false, [1 => ['answer' => '3.14']]); 286 $attemptobj->process_finish($timenow, false); 287 288 // User 2 goes overdue in quiz 1. 289 $attempt = quiz_create_attempt($quizobj1b, 1, false, $timenow, false, $u2->id); 290 quiz_start_new_attempt($quizobj1b, $quba1b, $attempt, 1, $timenow); 291 quiz_attempt_save_started($quizobj1b, $quba1b, $attempt); 292 $attemptobj = quiz_attempt::create($attempt->id); 293 $attemptobj->process_going_overdue($timenow, true); 294 295 // User 3 does not finish quiz 1. 296 $attempt = quiz_create_attempt($quizobj1c, 1, false, $timenow, false, $u3->id); 297 quiz_start_new_attempt($quizobj1c, $quba1c, $attempt, 1, $timenow); 298 quiz_attempt_save_started($quizobj1c, $quba1c, $attempt); 299 300 // User 4 abandons the quiz 1. 301 $attempt = quiz_create_attempt($quizobj1d, 1, false, $timenow, false, $u4->id); 302 quiz_start_new_attempt($quizobj1d, $quba1d, $attempt, 1, $timenow); 303 quiz_attempt_save_started($quizobj1d, $quba1d, $attempt); 304 $attemptobj = quiz_attempt::create($attempt->id); 305 $attemptobj->process_abandon($timenow, true); 306 307 // User 1 attempts the quiz three times (abandon, finish, in progress). 308 $quba2a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context()); 309 $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour); 310 311 $attempt = quiz_create_attempt($quizobj2a, 1, false, $timenow, false, $u1->id); 312 quiz_start_new_attempt($quizobj2a, $quba2a, $attempt, 1, $timenow); 313 quiz_attempt_save_started($quizobj2a, $quba2a, $attempt); 314 $attemptobj = quiz_attempt::create($attempt->id); 315 $attemptobj->process_abandon($timenow, true); 316 317 $quba2a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context()); 318 $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour); 319 320 $attempt = quiz_create_attempt($quizobj2a, 2, false, $timenow, false, $u1->id); 321 quiz_start_new_attempt($quizobj2a, $quba2a, $attempt, 2, $timenow); 322 quiz_attempt_save_started($quizobj2a, $quba2a, $attempt); 323 $attemptobj = quiz_attempt::create($attempt->id); 324 $attemptobj->process_finish($timenow, false); 325 326 $quba2a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context()); 327 $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour); 328 329 $attempt = quiz_create_attempt($quizobj2a, 3, false, $timenow, false, $u1->id); 330 quiz_start_new_attempt($quizobj2a, $quba2a, $attempt, 3, $timenow); 331 quiz_attempt_save_started($quizobj2a, $quba2a, $attempt); 332 333 // Check for user 1. 334 $attempts = quiz_get_user_attempts($quiz1->id, $u1->id, 'all'); 335 $this->assertCount(1, $attempts); 336 $attempt = array_shift($attempts); 337 $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); 338 $this->assertEquals($u1->id, $attempt->userid); 339 $this->assertEquals($quiz1->id, $attempt->quiz); 340 341 $attempts = quiz_get_user_attempts($quiz1->id, $u1->id, 'finished'); 342 $this->assertCount(1, $attempts); 343 $attempt = array_shift($attempts); 344 $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); 345 $this->assertEquals($u1->id, $attempt->userid); 346 $this->assertEquals($quiz1->id, $attempt->quiz); 347 348 $attempts = quiz_get_user_attempts($quiz1->id, $u1->id, 'unfinished'); 349 $this->assertCount(0, $attempts); 350 351 // Check for user 2. 352 $attempts = quiz_get_user_attempts($quiz1->id, $u2->id, 'all'); 353 $this->assertCount(1, $attempts); 354 $attempt = array_shift($attempts); 355 $this->assertEquals(quiz_attempt::OVERDUE, $attempt->state); 356 $this->assertEquals($u2->id, $attempt->userid); 357 $this->assertEquals($quiz1->id, $attempt->quiz); 358 359 $attempts = quiz_get_user_attempts($quiz1->id, $u2->id, 'finished'); 360 $this->assertCount(0, $attempts); 361 362 $attempts = quiz_get_user_attempts($quiz1->id, $u2->id, 'unfinished'); 363 $this->assertCount(1, $attempts); 364 $attempt = array_shift($attempts); 365 $this->assertEquals(quiz_attempt::OVERDUE, $attempt->state); 366 $this->assertEquals($u2->id, $attempt->userid); 367 $this->assertEquals($quiz1->id, $attempt->quiz); 368 369 // Check for user 3. 370 $attempts = quiz_get_user_attempts($quiz1->id, $u3->id, 'all'); 371 $this->assertCount(1, $attempts); 372 $attempt = array_shift($attempts); 373 $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state); 374 $this->assertEquals($u3->id, $attempt->userid); 375 $this->assertEquals($quiz1->id, $attempt->quiz); 376 377 $attempts = quiz_get_user_attempts($quiz1->id, $u3->id, 'finished'); 378 $this->assertCount(0, $attempts); 379 380 $attempts = quiz_get_user_attempts($quiz1->id, $u3->id, 'unfinished'); 381 $this->assertCount(1, $attempts); 382 $attempt = array_shift($attempts); 383 $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state); 384 $this->assertEquals($u3->id, $attempt->userid); 385 $this->assertEquals($quiz1->id, $attempt->quiz); 386 387 // Check for user 4. 388 $attempts = quiz_get_user_attempts($quiz1->id, $u4->id, 'all'); 389 $this->assertCount(1, $attempts); 390 $attempt = array_shift($attempts); 391 $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); 392 $this->assertEquals($u4->id, $attempt->userid); 393 $this->assertEquals($quiz1->id, $attempt->quiz); 394 395 $attempts = quiz_get_user_attempts($quiz1->id, $u4->id, 'finished'); 396 $this->assertCount(1, $attempts); 397 $attempt = array_shift($attempts); 398 $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); 399 $this->assertEquals($u4->id, $attempt->userid); 400 $this->assertEquals($quiz1->id, $attempt->quiz); 401 402 $attempts = quiz_get_user_attempts($quiz1->id, $u4->id, 'unfinished'); 403 $this->assertCount(0, $attempts); 404 405 // Multiple attempts for user 1 in quiz 2. 406 $attempts = quiz_get_user_attempts($quiz2->id, $u1->id, 'all'); 407 $this->assertCount(3, $attempts); 408 $attempt = array_shift($attempts); 409 $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); 410 $this->assertEquals($u1->id, $attempt->userid); 411 $this->assertEquals($quiz2->id, $attempt->quiz); 412 $attempt = array_shift($attempts); 413 $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); 414 $this->assertEquals($u1->id, $attempt->userid); 415 $this->assertEquals($quiz2->id, $attempt->quiz); 416 $attempt = array_shift($attempts); 417 $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state); 418 $this->assertEquals($u1->id, $attempt->userid); 419 $this->assertEquals($quiz2->id, $attempt->quiz); 420 421 $attempts = quiz_get_user_attempts($quiz2->id, $u1->id, 'finished'); 422 $this->assertCount(2, $attempts); 423 $attempt = array_shift($attempts); 424 $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); 425 $attempt = array_shift($attempts); 426 $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); 427 428 $attempts = quiz_get_user_attempts($quiz2->id, $u1->id, 'unfinished'); 429 $this->assertCount(1, $attempts); 430 $attempt = array_shift($attempts); 431 432 // Multiple quiz attempts fetched at once. 433 $attempts = quiz_get_user_attempts([$quiz1->id, $quiz2->id], $u1->id, 'all'); 434 $this->assertCount(4, $attempts); 435 $attempt = array_shift($attempts); 436 $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); 437 $this->assertEquals($u1->id, $attempt->userid); 438 $this->assertEquals($quiz1->id, $attempt->quiz); 439 $attempt = array_shift($attempts); 440 $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); 441 $this->assertEquals($u1->id, $attempt->userid); 442 $this->assertEquals($quiz2->id, $attempt->quiz); 443 $attempt = array_shift($attempts); 444 $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); 445 $this->assertEquals($u1->id, $attempt->userid); 446 $this->assertEquals($quiz2->id, $attempt->quiz); 447 $attempt = array_shift($attempts); 448 $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state); 449 $this->assertEquals($u1->id, $attempt->userid); 450 $this->assertEquals($quiz2->id, $attempt->quiz); 451 } 452 453 /** 454 * Test for quiz_get_group_override_priorities(). 455 */ 456 public function test_quiz_get_group_override_priorities() { 457 global $DB; 458 $this->resetAfterTest(); 459 460 $dg = $this->getDataGenerator(); 461 $quizgen = $dg->get_plugin_generator('mod_quiz'); 462 $course = $dg->create_course(); 463 464 $quiz = $quizgen->create_instance(['course' => $course->id, 'sumgrades' => 2]); 465 466 $this->assertNull(quiz_get_group_override_priorities($quiz->id)); 467 468 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 469 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id)); 470 471 $now = 100; 472 $override1 = (object)[ 473 'quiz' => $quiz->id, 474 'groupid' => $group1->id, 475 'timeopen' => $now, 476 'timeclose' => $now + 20 477 ]; 478 $DB->insert_record('quiz_overrides', $override1); 479 480 $override2 = (object)[ 481 'quiz' => $quiz->id, 482 'groupid' => $group2->id, 483 'timeopen' => $now - 10, 484 'timeclose' => $now + 10 485 ]; 486 $DB->insert_record('quiz_overrides', $override2); 487 488 $priorities = quiz_get_group_override_priorities($quiz->id); 489 $this->assertNotEmpty($priorities); 490 491 $openpriorities = $priorities['open']; 492 // Override 2's time open has higher priority since it is sooner than override 1's. 493 $this->assertEquals(2, $openpriorities[$override1->timeopen]); 494 $this->assertEquals(1, $openpriorities[$override2->timeopen]); 495 496 $closepriorities = $priorities['close']; 497 // Override 1's time close has higher priority since it is later than override 2's. 498 $this->assertEquals(1, $closepriorities[$override1->timeclose]); 499 $this->assertEquals(2, $closepriorities[$override2->timeclose]); 500 } 501 502 public function test_quiz_core_calendar_provide_event_action_open() { 503 $this->resetAfterTest(); 504 505 $this->setAdminUser(); 506 507 // Create a course. 508 $course = $this->getDataGenerator()->create_course(); 509 // Create a student and enrol into the course. 510 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 511 // Create a quiz. 512 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id, 513 'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS)); 514 515 // Create a calendar event. 516 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN); 517 // Now, log in as student. 518 $this->setUser($student); 519 // Create an action factory. 520 $factory = new \core_calendar\action_factory(); 521 522 // Decorate action event. 523 $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory); 524 525 // Confirm the event was decorated. 526 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 527 $this->assertEquals(get_string('attemptquiznow', 'quiz'), $actionevent->get_name()); 528 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 529 $this->assertEquals(1, $actionevent->get_item_count()); 530 $this->assertTrue($actionevent->is_actionable()); 531 } 532 533 public function test_quiz_core_calendar_provide_event_action_open_for_user() { 534 $this->resetAfterTest(); 535 536 $this->setAdminUser(); 537 538 // Create a course. 539 $course = $this->getDataGenerator()->create_course(); 540 // Create a student and enrol into the course. 541 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 542 // Create a quiz. 543 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id, 544 'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS)); 545 546 // Create a calendar event. 547 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN); 548 549 // Create an action factory. 550 $factory = new \core_calendar\action_factory(); 551 552 // Decorate action event for the student. 553 $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id); 554 555 // Confirm the event was decorated. 556 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 557 $this->assertEquals(get_string('attemptquiznow', 'quiz'), $actionevent->get_name()); 558 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 559 $this->assertEquals(1, $actionevent->get_item_count()); 560 $this->assertTrue($actionevent->is_actionable()); 561 } 562 563 public function test_quiz_core_calendar_provide_event_action_closed() { 564 $this->resetAfterTest(); 565 566 $this->setAdminUser(); 567 568 // Create a course. 569 $course = $this->getDataGenerator()->create_course(); 570 571 // Create a quiz. 572 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id, 573 'timeclose' => time() - DAYSECS)); 574 575 // Create a calendar event. 576 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_CLOSE); 577 578 // Create an action factory. 579 $factory = new \core_calendar\action_factory(); 580 581 // Confirm the result was null. 582 $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory)); 583 } 584 585 public function test_quiz_core_calendar_provide_event_action_closed_for_user() { 586 $this->resetAfterTest(); 587 588 $this->setAdminUser(); 589 590 // Create a course. 591 $course = $this->getDataGenerator()->create_course(); 592 593 // Create a student. 594 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 595 596 // Create a quiz. 597 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id, 598 'timeclose' => time() - DAYSECS)); 599 600 // Create a calendar event. 601 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_CLOSE); 602 603 // Create an action factory. 604 $factory = new \core_calendar\action_factory(); 605 606 // Confirm the result was null. 607 $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id)); 608 } 609 610 public function test_quiz_core_calendar_provide_event_action_open_in_future() { 611 $this->resetAfterTest(); 612 613 $this->setAdminUser(); 614 615 // Create a course. 616 $course = $this->getDataGenerator()->create_course(); 617 // Create a student and enrol into the course. 618 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 619 // Create a quiz. 620 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id, 621 'timeopen' => time() + DAYSECS)); 622 623 // Create a calendar event. 624 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_CLOSE); 625 // Now, log in as student. 626 $this->setUser($student); 627 // Create an action factory. 628 $factory = new \core_calendar\action_factory(); 629 630 // Decorate action event. 631 $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory); 632 633 // Confirm the event was decorated. 634 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 635 $this->assertEquals(get_string('attemptquiznow', 'quiz'), $actionevent->get_name()); 636 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 637 $this->assertEquals(1, $actionevent->get_item_count()); 638 $this->assertFalse($actionevent->is_actionable()); 639 } 640 641 public function test_quiz_core_calendar_provide_event_action_open_in_future_for_user() { 642 $this->resetAfterTest(); 643 644 $this->setAdminUser(); 645 646 // Create a course. 647 $course = $this->getDataGenerator()->create_course(); 648 // Create a student and enrol into the course. 649 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 650 // Create a quiz. 651 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id, 652 'timeopen' => time() + DAYSECS)); 653 654 // Create a calendar event. 655 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_CLOSE); 656 657 // Create an action factory. 658 $factory = new \core_calendar\action_factory(); 659 660 // Decorate action event for the student. 661 $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id); 662 663 // Confirm the event was decorated. 664 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 665 $this->assertEquals(get_string('attemptquiznow', 'quiz'), $actionevent->get_name()); 666 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 667 $this->assertEquals(1, $actionevent->get_item_count()); 668 $this->assertFalse($actionevent->is_actionable()); 669 } 670 671 public function test_quiz_core_calendar_provide_event_action_no_capability() { 672 global $DB; 673 674 $this->resetAfterTest(); 675 $this->setAdminUser(); 676 677 // Create a course. 678 $course = $this->getDataGenerator()->create_course(); 679 680 // Create a student. 681 $student = $this->getDataGenerator()->create_user(); 682 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 683 684 // Enrol student. 685 $this->assertTrue($this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id)); 686 687 // Create a quiz. 688 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id)); 689 690 // Remove the permission to attempt or review the quiz for the student role. 691 $coursecontext = context_course::instance($course->id); 692 assign_capability('mod/quiz:reviewmyattempts', CAP_PROHIBIT, $studentrole->id, $coursecontext); 693 assign_capability('mod/quiz:attempt', CAP_PROHIBIT, $studentrole->id, $coursecontext); 694 695 // Create a calendar event. 696 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN); 697 698 // Create an action factory. 699 $factory = new \core_calendar\action_factory(); 700 701 // Set current user to the student. 702 $this->setUser($student); 703 704 // Confirm null is returned. 705 $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory)); 706 } 707 708 public function test_quiz_core_calendar_provide_event_action_no_capability_for_user() { 709 global $DB; 710 711 $this->resetAfterTest(); 712 $this->setAdminUser(); 713 714 // Create a course. 715 $course = $this->getDataGenerator()->create_course(); 716 717 // Create a student. 718 $student = $this->getDataGenerator()->create_user(); 719 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 720 721 // Enrol student. 722 $this->assertTrue($this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id)); 723 724 // Create a quiz. 725 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id)); 726 727 // Remove the permission to attempt or review the quiz for the student role. 728 $coursecontext = context_course::instance($course->id); 729 assign_capability('mod/quiz:reviewmyattempts', CAP_PROHIBIT, $studentrole->id, $coursecontext); 730 assign_capability('mod/quiz:attempt', CAP_PROHIBIT, $studentrole->id, $coursecontext); 731 732 // Create a calendar event. 733 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN); 734 735 // Create an action factory. 736 $factory = new \core_calendar\action_factory(); 737 738 // Confirm null is returned. 739 $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id)); 740 } 741 742 public function test_quiz_core_calendar_provide_event_action_already_finished() { 743 global $DB; 744 745 $this->resetAfterTest(); 746 747 $this->setAdminUser(); 748 749 // Create a course. 750 $course = $this->getDataGenerator()->create_course(); 751 752 // Create a student. 753 $student = $this->getDataGenerator()->create_user(); 754 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 755 756 // Enrol student. 757 $this->assertTrue($this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id)); 758 759 // Create a quiz. 760 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id, 761 'sumgrades' => 1)); 762 763 // Add a question to the quiz. 764 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 765 $cat = $questiongenerator->create_question_category(); 766 $question = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); 767 quiz_add_quiz_question($question->id, $quiz); 768 769 // Get the quiz object. 770 $quizobj = quiz::create($quiz->id, $student->id); 771 772 // Create an attempt for the student in the quiz. 773 $timenow = time(); 774 $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $student->id); 775 $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); 776 $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); 777 quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow); 778 quiz_attempt_save_started($quizobj, $quba, $attempt); 779 780 // Finish the attempt. 781 $attemptobj = quiz_attempt::create($attempt->id); 782 $attemptobj->process_finish($timenow, false); 783 784 // Create a calendar event. 785 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN); 786 787 // Create an action factory. 788 $factory = new \core_calendar\action_factory(); 789 790 // Set current user to the student. 791 $this->setUser($student); 792 793 // Confirm null is returned. 794 $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory)); 795 } 796 797 public function test_quiz_core_calendar_provide_event_action_already_finished_for_user() { 798 global $DB; 799 800 $this->resetAfterTest(); 801 802 $this->setAdminUser(); 803 804 // Create a course. 805 $course = $this->getDataGenerator()->create_course(); 806 807 // Create a student. 808 $student = $this->getDataGenerator()->create_user(); 809 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 810 811 // Enrol student. 812 $this->assertTrue($this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id)); 813 814 // Create a quiz. 815 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id, 816 'sumgrades' => 1)); 817 818 // Add a question to the quiz. 819 $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); 820 $cat = $questiongenerator->create_question_category(); 821 $question = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); 822 quiz_add_quiz_question($question->id, $quiz); 823 824 // Get the quiz object. 825 $quizobj = quiz::create($quiz->id, $student->id); 826 827 // Create an attempt for the student in the quiz. 828 $timenow = time(); 829 $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $student->id); 830 $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); 831 $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); 832 quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow); 833 quiz_attempt_save_started($quizobj, $quba, $attempt); 834 835 // Finish the attempt. 836 $attemptobj = quiz_attempt::create($attempt->id); 837 $attemptobj->process_finish($timenow, false); 838 839 // Create a calendar event. 840 $event = $this->create_action_event($course->id, $quiz->id, QUIZ_EVENT_TYPE_OPEN); 841 842 // Create an action factory. 843 $factory = new \core_calendar\action_factory(); 844 845 // Confirm null is returned. 846 $this->assertNull(mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id)); 847 } 848 849 public function test_quiz_core_calendar_provide_event_action_already_completed() { 850 $this->resetAfterTest(); 851 set_config('enablecompletion', 1); 852 $this->setAdminUser(); 853 854 // Create the activity. 855 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 856 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id), 857 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 858 859 // Get some additional data. 860 $cm = get_coursemodule_from_instance('quiz', $quiz->id); 861 862 // Create a calendar event. 863 $event = $this->create_action_event($course->id, $quiz->id, 864 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 865 866 // Mark the activity as completed. 867 $completion = new completion_info($course); 868 $completion->set_module_viewed($cm); 869 870 // Create an action factory. 871 $factory = new \core_calendar\action_factory(); 872 873 // Decorate action event. 874 $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory); 875 876 // Ensure result was null. 877 $this->assertNull($actionevent); 878 } 879 880 public function test_quiz_core_calendar_provide_event_action_already_completed_for_user() { 881 $this->resetAfterTest(); 882 set_config('enablecompletion', 1); 883 $this->setAdminUser(); 884 885 // Create the activity. 886 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 887 $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id), 888 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 889 890 // Enrol a student in the course. 891 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 892 893 // Get some additional data. 894 $cm = get_coursemodule_from_instance('quiz', $quiz->id); 895 896 // Create a calendar event. 897 $event = $this->create_action_event($course->id, $quiz->id, 898 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 899 900 // Mark the activity as completed for the student. 901 $completion = new completion_info($course); 902 $completion->set_module_viewed($cm, $student->id); 903 904 // Create an action factory. 905 $factory = new \core_calendar\action_factory(); 906 907 // Decorate action event for the student. 908 $actionevent = mod_quiz_core_calendar_provide_event_action($event, $factory, $student->id); 909 910 // Ensure result was null. 911 $this->assertNull($actionevent); 912 } 913 914 /** 915 * Creates an action event. 916 * 917 * @param int $courseid 918 * @param int $instanceid The quiz id. 919 * @param string $eventtype The event type. eg. QUIZ_EVENT_TYPE_OPEN. 920 * @return bool|calendar_event 921 */ 922 private function create_action_event($courseid, $instanceid, $eventtype) { 923 $event = new stdClass(); 924 $event->name = 'Calendar event'; 925 $event->modulename = 'quiz'; 926 $event->courseid = $courseid; 927 $event->instance = $instanceid; 928 $event->type = CALENDAR_EVENT_TYPE_ACTION; 929 $event->eventtype = $eventtype; 930 $event->timestart = time(); 931 932 return calendar_event::create($event); 933 } 934 935 /** 936 * Test the callback responsible for returning the completion rule descriptions. 937 * This function should work given either an instance of the module (cm_info), such as when checking the active rules, 938 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type. 939 */ 940 public function test_mod_quiz_completion_get_active_rule_descriptions() { 941 $this->resetAfterTest(); 942 $this->setAdminUser(); 943 944 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't. 945 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]); 946 $quiz1 = $this->getDataGenerator()->create_module('quiz', [ 947 'course' => $course->id, 948 'completion' => 2, 949 'completionusegrade' => 1, 950 'completionattemptsexhausted' => 1, 951 'completionpass' => 1 952 ]); 953 $quiz2 = $this->getDataGenerator()->create_module('quiz', [ 954 'course' => $course->id, 955 'completion' => 2, 956 'completionusegrade' => 0 957 ]); 958 $cm1 = cm_info::create(get_coursemodule_from_instance('quiz', $quiz1->id)); 959 $cm2 = cm_info::create(get_coursemodule_from_instance('quiz', $quiz2->id)); 960 961 // Data for the stdClass input type. 962 // This type of input would occur when checking the default completion rules for an activity type, where we don't have 963 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. 964 $moddefaults = new stdClass(); 965 $moddefaults->customdata = ['customcompletionrules' => [ 966 'completionattemptsexhausted' => 1, 967 'completionpass' => 1 968 ]]; 969 $moddefaults->completion = 2; 970 971 $activeruledescriptions = [ 972 get_string('completionattemptsexhausteddesc', 'quiz'), 973 get_string('completionpassdesc', 'quiz'), 974 ]; 975 $this->assertEquals(mod_quiz_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); 976 $this->assertEquals(mod_quiz_get_completion_active_rule_descriptions($cm2), []); 977 $this->assertEquals(mod_quiz_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); 978 $this->assertEquals(mod_quiz_get_completion_active_rule_descriptions(new stdClass()), []); 979 } 980 981 /** 982 * A user who does not have capabilities to add events to the calendar should be able to create a quiz. 983 */ 984 public function test_creation_with_no_calendar_capabilities() { 985 $this->resetAfterTest(); 986 $course = self::getDataGenerator()->create_course(); 987 $context = context_course::instance($course->id); 988 $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher'); 989 $roleid = self::getDataGenerator()->create_role(); 990 self::getDataGenerator()->role_assign($roleid, $user->id, $context->id); 991 assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); 992 $generator = self::getDataGenerator()->get_plugin_generator('mod_quiz'); 993 // Create an instance as a user without the calendar capabilities. 994 $this->setUser($user); 995 $time = time(); 996 $params = array( 997 'course' => $course->id, 998 'timeopen' => $time + 200, 999 'timeclose' => $time + 2000, 1000 ); 1001 $generator->create_instance($params); 1002 } 1003 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body