Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 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   * Quiz events tests.
  19   *
  20   * @package    mod_quiz
  21   * @category   phpunit
  22   * @copyright  2013 Adrian Greeve
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace mod_quiz\event;
  27  
  28  use quiz;
  29  use quiz_attempt;
  30  
  31  defined('MOODLE_INTERNAL') || die();
  32  
  33  global $CFG;
  34  require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
  35  
  36  /**
  37   * Unit tests for quiz events.
  38   *
  39   * @package    mod_quiz
  40   * @category   phpunit
  41   * @copyright  2013 Adrian Greeve
  42   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  43   */
  44  class events_test extends \advanced_testcase {
  45  
  46      /**
  47       * Setup a quiz.
  48       *
  49       * @return quiz the generated quiz.
  50       */
  51      protected function prepare_quiz() {
  52  
  53          $this->resetAfterTest(true);
  54  
  55          // Create a course
  56          $course = $this->getDataGenerator()->create_course();
  57  
  58          // Make a quiz.
  59          $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
  60  
  61          $quiz = $quizgenerator->create_instance(array('course' => $course->id, 'questionsperpage' => 0,
  62                  'grade' => 100.0, 'sumgrades' => 2));
  63  
  64          $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id);
  65  
  66          // Create a couple of questions.
  67          $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
  68  
  69          $cat = $questiongenerator->create_question_category();
  70          $saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
  71          $numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
  72  
  73          // Add them to the quiz.
  74          quiz_add_quiz_question($saq->id, $quiz);
  75          quiz_add_quiz_question($numq->id, $quiz);
  76  
  77          // Make a user to do the quiz.
  78          $user1 = $this->getDataGenerator()->create_user();
  79          $this->setUser($user1);
  80  
  81          return quiz::create($quiz->id, $user1->id);
  82      }
  83  
  84      /**
  85       * Setup a quiz attempt at the quiz created by {@link prepare_quiz()}.
  86       *
  87       * @param quiz $quizobj the generated quiz.
  88       * @param bool $ispreview Make the attempt a preview attempt when true.
  89       * @return array with three elements, array($quizobj, $quba, $attempt)
  90       */
  91      protected function prepare_quiz_attempt($quizobj, $ispreview = false) {
  92          // Start the attempt.
  93          $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
  94          $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
  95  
  96          $timenow = time();
  97          $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, $ispreview);
  98          quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
  99          quiz_attempt_save_started($quizobj, $quba, $attempt);
 100  
 101          return array($quizobj, $quba, $attempt);
 102      }
 103  
 104      /**
 105       * Setup some convenience test data with a single attempt.
 106       *
 107       * @param bool $ispreview Make the attempt a preview attempt when true.
 108       * @return array with three elements, array($quizobj, $quba, $attempt)
 109       */
 110      protected function prepare_quiz_data($ispreview = false) {
 111          $quizobj = $this->prepare_quiz();
 112          return $this->prepare_quiz_attempt($quizobj, $ispreview);
 113      }
 114  
 115      public function test_attempt_submitted() {
 116  
 117          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 118          $attemptobj = quiz_attempt::create($attempt->id);
 119  
 120          // Catch the event.
 121          $sink = $this->redirectEvents();
 122  
 123          $timefinish = time();
 124          $attemptobj->process_finish($timefinish, false);
 125          $events = $sink->get_events();
 126          $sink->close();
 127  
 128          // Validate the event.
 129          $this->assertCount(3, $events);
 130          $event = $events[2];
 131          $this->assertInstanceOf('\mod_quiz\event\attempt_submitted', $event);
 132          $this->assertEquals('quiz_attempts', $event->objecttable);
 133          $this->assertEquals($quizobj->get_context(), $event->get_context());
 134          $this->assertEquals($attempt->userid, $event->relateduserid);
 135          $this->assertEquals(null, $event->other['submitterid']); // Should be the user, but PHP Unit complains...
 136          $this->assertEquals('quiz_attempt_submitted', $event->get_legacy_eventname());
 137          $legacydata = new \stdClass();
 138          $legacydata->component = 'mod_quiz';
 139          $legacydata->attemptid = (string) $attempt->id;
 140          $legacydata->timestamp = $timefinish;
 141          $legacydata->userid = $attempt->userid;
 142          $legacydata->cmid = $quizobj->get_cmid();
 143          $legacydata->courseid = $quizobj->get_courseid();
 144          $legacydata->quizid = $quizobj->get_quizid();
 145          // Submitterid should be the user, but as we are in PHP Unit, CLI_SCRIPT is set to true which sets null in submitterid.
 146          $legacydata->submitterid = null;
 147          $legacydata->timefinish = $timefinish;
 148          $this->assertEventLegacyData($legacydata, $event);
 149          $this->assertEventContextNotUsed($event);
 150      }
 151  
 152      public function test_attempt_becameoverdue() {
 153  
 154          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 155          $attemptobj = quiz_attempt::create($attempt->id);
 156  
 157          // Catch the event.
 158          $sink = $this->redirectEvents();
 159          $timefinish = time();
 160          $attemptobj->process_going_overdue($timefinish, false);
 161          $events = $sink->get_events();
 162          $sink->close();
 163  
 164          $this->assertCount(1, $events);
 165          $event = $events[0];
 166          $this->assertInstanceOf('\mod_quiz\event\attempt_becameoverdue', $event);
 167          $this->assertEquals('quiz_attempts', $event->objecttable);
 168          $this->assertEquals($quizobj->get_context(), $event->get_context());
 169          $this->assertEquals($attempt->userid, $event->relateduserid);
 170          $this->assertNotEmpty($event->get_description());
 171          // Submitterid should be the user, but as we are in PHP Unit, CLI_SCRIPT is set to true which sets null in submitterid.
 172          $this->assertEquals(null, $event->other['submitterid']);
 173          $this->assertEquals('quiz_attempt_overdue', $event->get_legacy_eventname());
 174          $legacydata = new \stdClass();
 175          $legacydata->component = 'mod_quiz';
 176          $legacydata->attemptid = (string) $attempt->id;
 177          $legacydata->timestamp = $timefinish;
 178          $legacydata->userid = $attempt->userid;
 179          $legacydata->cmid = $quizobj->get_cmid();
 180          $legacydata->courseid = $quizobj->get_courseid();
 181          $legacydata->quizid = $quizobj->get_quizid();
 182          $legacydata->submitterid = null; // Should be the user, but PHP Unit complains...
 183          $this->assertEventLegacyData($legacydata, $event);
 184          $this->assertEventContextNotUsed($event);
 185      }
 186  
 187      public function test_attempt_abandoned() {
 188  
 189          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 190          $attemptobj = quiz_attempt::create($attempt->id);
 191  
 192          // Catch the event.
 193          $sink = $this->redirectEvents();
 194          $timefinish = time();
 195          $attemptobj->process_abandon($timefinish, false);
 196          $events = $sink->get_events();
 197          $sink->close();
 198  
 199          $this->assertCount(1, $events);
 200          $event = $events[0];
 201          $this->assertInstanceOf('\mod_quiz\event\attempt_abandoned', $event);
 202          $this->assertEquals('quiz_attempts', $event->objecttable);
 203          $this->assertEquals($quizobj->get_context(), $event->get_context());
 204          $this->assertEquals($attempt->userid, $event->relateduserid);
 205          // Submitterid should be the user, but as we are in PHP Unit, CLI_SCRIPT is set to true which sets null in submitterid.
 206          $this->assertEquals(null, $event->other['submitterid']);
 207          $this->assertEquals('quiz_attempt_abandoned', $event->get_legacy_eventname());
 208          $legacydata = new \stdClass();
 209          $legacydata->component = 'mod_quiz';
 210          $legacydata->attemptid = (string) $attempt->id;
 211          $legacydata->timestamp = $timefinish;
 212          $legacydata->userid = $attempt->userid;
 213          $legacydata->cmid = $quizobj->get_cmid();
 214          $legacydata->courseid = $quizobj->get_courseid();
 215          $legacydata->quizid = $quizobj->get_quizid();
 216          $legacydata->submitterid = null; // Should be the user, but PHP Unit complains...
 217          $this->assertEventLegacyData($legacydata, $event);
 218          $this->assertEventContextNotUsed($event);
 219      }
 220  
 221      public function test_attempt_started() {
 222          $quizobj = $this->prepare_quiz();
 223  
 224          $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
 225          $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
 226  
 227          $timenow = time();
 228          $attempt = quiz_create_attempt($quizobj, 1, false, $timenow);
 229          quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
 230  
 231          // Trigger and capture the event.
 232          $sink = $this->redirectEvents();
 233          quiz_attempt_save_started($quizobj, $quba, $attempt);
 234          $events = $sink->get_events();
 235          $event = reset($events);
 236  
 237          // Check that the event data is valid.
 238          $this->assertInstanceOf('\mod_quiz\event\attempt_started', $event);
 239          $this->assertEquals('quiz_attempts', $event->objecttable);
 240          $this->assertEquals($attempt->id, $event->objectid);
 241          $this->assertEquals($attempt->userid, $event->relateduserid);
 242          $this->assertEquals($quizobj->get_context(), $event->get_context());
 243          $this->assertEquals('quiz_attempt_started', $event->get_legacy_eventname());
 244          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 245          // Check legacy log data.
 246          $expected = array($quizobj->get_courseid(), 'quiz', 'attempt', 'review.php?attempt=' . $attempt->id,
 247              $quizobj->get_quizid(), $quizobj->get_cmid());
 248          $this->assertEventLegacyLogData($expected, $event);
 249          // Check legacy event data.
 250          $legacydata = new \stdClass();
 251          $legacydata->component = 'mod_quiz';
 252          $legacydata->attemptid = $attempt->id;
 253          $legacydata->timestart = $attempt->timestart;
 254          $legacydata->timestamp = $attempt->timestart;
 255          $legacydata->userid = $attempt->userid;
 256          $legacydata->quizid = $quizobj->get_quizid();
 257          $legacydata->cmid = $quizobj->get_cmid();
 258          $legacydata->courseid = $quizobj->get_courseid();
 259          $this->assertEventLegacyData($legacydata, $event);
 260          $this->assertEventContextNotUsed($event);
 261      }
 262  
 263      /**
 264       * Test the edit page viewed event.
 265       *
 266       * There is no external API for updating a quiz, so the unit test will simply
 267       * create and trigger the event and ensure the event data is returned as expected.
 268       */
 269      public function test_edit_page_viewed() {
 270          $this->resetAfterTest();
 271  
 272          $this->setAdminUser();
 273          $course = $this->getDataGenerator()->create_course();
 274          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 275  
 276          $params = array(
 277              'courseid' => $course->id,
 278              'context' => \context_module::instance($quiz->cmid),
 279              'other' => array(
 280                  'quizid' => $quiz->id
 281              )
 282          );
 283          $event = \mod_quiz\event\edit_page_viewed::create($params);
 284  
 285          // Trigger and capture the event.
 286          $sink = $this->redirectEvents();
 287          $event->trigger();
 288          $events = $sink->get_events();
 289          $event = reset($events);
 290  
 291          // Check that the event data is valid.
 292          $this->assertInstanceOf('\mod_quiz\event\edit_page_viewed', $event);
 293          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 294          $expected = array($course->id, 'quiz', 'editquestions', 'view.php?id=' . $quiz->cmid, $quiz->id, $quiz->cmid);
 295          $this->assertEventLegacyLogData($expected, $event);
 296          $this->assertEventContextNotUsed($event);
 297      }
 298  
 299      /**
 300       * Test the attempt deleted event.
 301       */
 302      public function test_attempt_deleted() {
 303          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 304  
 305          // Trigger and capture the event.
 306          $sink = $this->redirectEvents();
 307          quiz_delete_attempt($attempt, $quizobj->get_quiz());
 308          $events = $sink->get_events();
 309          $event = reset($events);
 310  
 311          // Check that the event data is valid.
 312          $this->assertInstanceOf('\mod_quiz\event\attempt_deleted', $event);
 313          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 314          $expected = array($quizobj->get_courseid(), 'quiz', 'delete attempt', 'report.php?id=' . $quizobj->get_cmid(),
 315              $attempt->id, $quizobj->get_cmid());
 316          $this->assertEventLegacyLogData($expected, $event);
 317          $this->assertEventContextNotUsed($event);
 318      }
 319  
 320      /**
 321       * Test that preview attempt deletions are not logged.
 322       */
 323      public function test_preview_attempt_deleted() {
 324          // Create quiz with preview attempt.
 325          list($quizobj, $quba, $previewattempt) = $this->prepare_quiz_data(true);
 326  
 327          // Delete a preview attempt, capturing events.
 328          $sink = $this->redirectEvents();
 329          quiz_delete_attempt($previewattempt, $quizobj->get_quiz());
 330  
 331          // Verify that no events were generated.
 332          $this->assertEmpty($sink->get_events());
 333      }
 334  
 335      /**
 336       * Test the report viewed event.
 337       *
 338       * There is no external API for viewing reports, so the unit test will simply
 339       * create and trigger the event and ensure the event data is returned as expected.
 340       */
 341      public function test_report_viewed() {
 342          $this->resetAfterTest();
 343  
 344          $this->setAdminUser();
 345          $course = $this->getDataGenerator()->create_course();
 346          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 347  
 348          $params = array(
 349              'context' => $context = \context_module::instance($quiz->cmid),
 350              'other' => array(
 351                  'quizid' => $quiz->id,
 352                  'reportname' => 'overview'
 353              )
 354          );
 355          $event = \mod_quiz\event\report_viewed::create($params);
 356  
 357          // Trigger and capture the event.
 358          $sink = $this->redirectEvents();
 359          $event->trigger();
 360          $events = $sink->get_events();
 361          $event = reset($events);
 362  
 363          // Check that the event data is valid.
 364          $this->assertInstanceOf('\mod_quiz\event\report_viewed', $event);
 365          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 366          $expected = array($course->id, 'quiz', 'report', 'report.php?id=' . $quiz->cmid . '&mode=overview',
 367              $quiz->id, $quiz->cmid);
 368          $this->assertEventLegacyLogData($expected, $event);
 369          $this->assertEventContextNotUsed($event);
 370      }
 371  
 372      /**
 373       * Test the attempt reviewed event.
 374       *
 375       * There is no external API for reviewing attempts, so the unit test will simply
 376       * create and trigger the event and ensure the event data is returned as expected.
 377       */
 378      public function test_attempt_reviewed() {
 379          $this->resetAfterTest();
 380  
 381          $this->setAdminUser();
 382          $course = $this->getDataGenerator()->create_course();
 383          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 384  
 385          $params = array(
 386              'objectid' => 1,
 387              'relateduserid' => 2,
 388              'courseid' => $course->id,
 389              'context' => \context_module::instance($quiz->cmid),
 390              'other' => array(
 391                  'quizid' => $quiz->id
 392              )
 393          );
 394          $event = \mod_quiz\event\attempt_reviewed::create($params);
 395  
 396          // Trigger and capture the event.
 397          $sink = $this->redirectEvents();
 398          $event->trigger();
 399          $events = $sink->get_events();
 400          $event = reset($events);
 401  
 402          // Check that the event data is valid.
 403          $this->assertInstanceOf('\mod_quiz\event\attempt_reviewed', $event);
 404          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 405          $expected = array($course->id, 'quiz', 'review', 'review.php?attempt=1', $quiz->id, $quiz->cmid);
 406          $this->assertEventLegacyLogData($expected, $event);
 407          $this->assertEventContextNotUsed($event);
 408      }
 409  
 410      /**
 411       * Test the attempt summary viewed event.
 412       *
 413       * There is no external API for viewing the attempt summary, so the unit test will simply
 414       * create and trigger the event and ensure the event data is returned as expected.
 415       */
 416      public function test_attempt_summary_viewed() {
 417          $this->resetAfterTest();
 418  
 419          $this->setAdminUser();
 420          $course = $this->getDataGenerator()->create_course();
 421          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 422  
 423          $params = array(
 424              'objectid' => 1,
 425              'relateduserid' => 2,
 426              'courseid' => $course->id,
 427              'context' => \context_module::instance($quiz->cmid),
 428              'other' => array(
 429                  'quizid' => $quiz->id
 430              )
 431          );
 432          $event = \mod_quiz\event\attempt_summary_viewed::create($params);
 433  
 434          // Trigger and capture the event.
 435          $sink = $this->redirectEvents();
 436          $event->trigger();
 437          $events = $sink->get_events();
 438          $event = reset($events);
 439  
 440          // Check that the event data is valid.
 441          $this->assertInstanceOf('\mod_quiz\event\attempt_summary_viewed', $event);
 442          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 443          $expected = array($course->id, 'quiz', 'view summary', 'summary.php?attempt=1', $quiz->id, $quiz->cmid);
 444          $this->assertEventLegacyLogData($expected, $event);
 445          $this->assertEventContextNotUsed($event);
 446      }
 447  
 448      /**
 449       * Test the user override created event.
 450       *
 451       * There is no external API for creating a user override, so the unit test will simply
 452       * create and trigger the event and ensure the event data is returned as expected.
 453       */
 454      public function test_user_override_created() {
 455          $this->resetAfterTest();
 456  
 457          $this->setAdminUser();
 458          $course = $this->getDataGenerator()->create_course();
 459          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 460  
 461          $params = array(
 462              'objectid' => 1,
 463              'relateduserid' => 2,
 464              'context' => \context_module::instance($quiz->cmid),
 465              'other' => array(
 466                  'quizid' => $quiz->id
 467              )
 468          );
 469          $event = \mod_quiz\event\user_override_created::create($params);
 470  
 471          // Trigger and capture the event.
 472          $sink = $this->redirectEvents();
 473          $event->trigger();
 474          $events = $sink->get_events();
 475          $event = reset($events);
 476  
 477          // Check that the event data is valid.
 478          $this->assertInstanceOf('\mod_quiz\event\user_override_created', $event);
 479          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 480          $this->assertEventContextNotUsed($event);
 481      }
 482  
 483      /**
 484       * Test the group override created event.
 485       *
 486       * There is no external API for creating a group override, so the unit test will simply
 487       * create and trigger the event and ensure the event data is returned as expected.
 488       */
 489      public function test_group_override_created() {
 490          $this->resetAfterTest();
 491  
 492          $this->setAdminUser();
 493          $course = $this->getDataGenerator()->create_course();
 494          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 495  
 496          $params = array(
 497              'objectid' => 1,
 498              'context' => \context_module::instance($quiz->cmid),
 499              'other' => array(
 500                  'quizid' => $quiz->id,
 501                  'groupid' => 2
 502              )
 503          );
 504          $event = \mod_quiz\event\group_override_created::create($params);
 505  
 506          // Trigger and capture the event.
 507          $sink = $this->redirectEvents();
 508          $event->trigger();
 509          $events = $sink->get_events();
 510          $event = reset($events);
 511  
 512          // Check that the event data is valid.
 513          $this->assertInstanceOf('\mod_quiz\event\group_override_created', $event);
 514          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 515          $this->assertEventContextNotUsed($event);
 516      }
 517  
 518      /**
 519       * Test the user override updated event.
 520       *
 521       * There is no external API for updating a user override, so the unit test will simply
 522       * create and trigger the event and ensure the event data is returned as expected.
 523       */
 524      public function test_user_override_updated() {
 525          $this->resetAfterTest();
 526  
 527          $this->setAdminUser();
 528          $course = $this->getDataGenerator()->create_course();
 529          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 530  
 531          $params = array(
 532              'objectid' => 1,
 533              'relateduserid' => 2,
 534              'context' => \context_module::instance($quiz->cmid),
 535              'other' => array(
 536                  'quizid' => $quiz->id
 537              )
 538          );
 539          $event = \mod_quiz\event\user_override_updated::create($params);
 540  
 541          // Trigger and capture the event.
 542          $sink = $this->redirectEvents();
 543          $event->trigger();
 544          $events = $sink->get_events();
 545          $event = reset($events);
 546  
 547          // Check that the event data is valid.
 548          $this->assertInstanceOf('\mod_quiz\event\user_override_updated', $event);
 549          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 550          $expected = array($course->id, 'quiz', 'edit override', 'overrideedit.php?id=1', $quiz->id, $quiz->cmid);
 551          $this->assertEventLegacyLogData($expected, $event);
 552          $this->assertEventContextNotUsed($event);
 553      }
 554  
 555      /**
 556       * Test the group override updated event.
 557       *
 558       * There is no external API for updating a group override, so the unit test will simply
 559       * create and trigger the event and ensure the event data is returned as expected.
 560       */
 561      public function test_group_override_updated() {
 562          $this->resetAfterTest();
 563  
 564          $this->setAdminUser();
 565          $course = $this->getDataGenerator()->create_course();
 566          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 567  
 568          $params = array(
 569              'objectid' => 1,
 570              'context' => \context_module::instance($quiz->cmid),
 571              'other' => array(
 572                  'quizid' => $quiz->id,
 573                  'groupid' => 2
 574              )
 575          );
 576          $event = \mod_quiz\event\group_override_updated::create($params);
 577  
 578          // Trigger and capture the event.
 579          $sink = $this->redirectEvents();
 580          $event->trigger();
 581          $events = $sink->get_events();
 582          $event = reset($events);
 583  
 584          // Check that the event data is valid.
 585          $this->assertInstanceOf('\mod_quiz\event\group_override_updated', $event);
 586          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 587          $expected = array($course->id, 'quiz', 'edit override', 'overrideedit.php?id=1', $quiz->id, $quiz->cmid);
 588          $this->assertEventLegacyLogData($expected, $event);
 589          $this->assertEventContextNotUsed($event);
 590      }
 591  
 592      /**
 593       * Test the user override deleted event.
 594       */
 595      public function test_user_override_deleted() {
 596          global $DB;
 597  
 598          $this->resetAfterTest();
 599  
 600          $this->setAdminUser();
 601          $course = $this->getDataGenerator()->create_course();
 602          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 603  
 604          // Create an override.
 605          $override = new \stdClass();
 606          $override->quiz = $quiz->id;
 607          $override->userid = 2;
 608          $override->id = $DB->insert_record('quiz_overrides', $override);
 609  
 610          // Trigger and capture the event.
 611          $sink = $this->redirectEvents();
 612          quiz_delete_override($quiz, $override->id);
 613          $events = $sink->get_events();
 614          $event = reset($events);
 615  
 616          // Check that the event data is valid.
 617          $this->assertInstanceOf('\mod_quiz\event\user_override_deleted', $event);
 618          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 619          $expected = array($course->id, 'quiz', 'delete override', 'overrides.php?cmid=' . $quiz->cmid, $quiz->id, $quiz->cmid);
 620          $this->assertEventLegacyLogData($expected, $event);
 621          $this->assertEventContextNotUsed($event);
 622      }
 623  
 624      /**
 625       * Test the group override deleted event.
 626       */
 627      public function test_group_override_deleted() {
 628          global $DB;
 629  
 630          $this->resetAfterTest();
 631  
 632          $this->setAdminUser();
 633          $course = $this->getDataGenerator()->create_course();
 634          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 635  
 636          // Create an override.
 637          $override = new \stdClass();
 638          $override->quiz = $quiz->id;
 639          $override->groupid = 2;
 640          $override->id = $DB->insert_record('quiz_overrides', $override);
 641  
 642          // Trigger and capture the event.
 643          $sink = $this->redirectEvents();
 644          quiz_delete_override($quiz, $override->id);
 645          $events = $sink->get_events();
 646          $event = reset($events);
 647  
 648          // Check that the event data is valid.
 649          $this->assertInstanceOf('\mod_quiz\event\group_override_deleted', $event);
 650          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 651          $expected = array($course->id, 'quiz', 'delete override', 'overrides.php?cmid=' . $quiz->cmid, $quiz->id, $quiz->cmid);
 652          $this->assertEventLegacyLogData($expected, $event);
 653          $this->assertEventContextNotUsed($event);
 654      }
 655  
 656      /**
 657       * Test the attempt viewed event.
 658       *
 659       * There is no external API for continuing an attempt, so the unit test will simply
 660       * create and trigger the event and ensure the event data is returned as expected.
 661       */
 662      public function test_attempt_viewed() {
 663          $this->resetAfterTest();
 664  
 665          $this->setAdminUser();
 666          $course = $this->getDataGenerator()->create_course();
 667          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 668  
 669          $params = array(
 670              'objectid' => 1,
 671              'relateduserid' => 2,
 672              'courseid' => $course->id,
 673              'context' => \context_module::instance($quiz->cmid),
 674              'other' => array(
 675                  'quizid' => $quiz->id
 676              )
 677          );
 678          $event = \mod_quiz\event\attempt_viewed::create($params);
 679  
 680          // Trigger and capture the event.
 681          $sink = $this->redirectEvents();
 682          $event->trigger();
 683          $events = $sink->get_events();
 684          $event = reset($events);
 685  
 686          // Check that the event data is valid.
 687          $this->assertInstanceOf('\mod_quiz\event\attempt_viewed', $event);
 688          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 689          $expected = array($course->id, 'quiz', 'continue attempt', 'review.php?attempt=1', $quiz->id, $quiz->cmid);
 690          $this->assertEventLegacyLogData($expected, $event);
 691          $this->assertEventContextNotUsed($event);
 692      }
 693  
 694      /**
 695       * Test the attempt previewed event.
 696       */
 697      public function test_attempt_preview_started() {
 698          $quizobj = $this->prepare_quiz();
 699  
 700          $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
 701          $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
 702  
 703          $timenow = time();
 704          $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, true);
 705          quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
 706  
 707          // Trigger and capture the event.
 708          $sink = $this->redirectEvents();
 709          quiz_attempt_save_started($quizobj, $quba, $attempt);
 710          $events = $sink->get_events();
 711          $event = reset($events);
 712  
 713          // Check that the event data is valid.
 714          $this->assertInstanceOf('\mod_quiz\event\attempt_preview_started', $event);
 715          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 716          $expected = array($quizobj->get_courseid(), 'quiz', 'preview', 'view.php?id=' . $quizobj->get_cmid(),
 717              $quizobj->get_quizid(), $quizobj->get_cmid());
 718          $this->assertEventLegacyLogData($expected, $event);
 719          $this->assertEventContextNotUsed($event);
 720      }
 721  
 722      /**
 723       * Test the question manually graded event.
 724       *
 725       * There is no external API for manually grading a question, so the unit test will simply
 726       * create and trigger the event and ensure the event data is returned as expected.
 727       */
 728      public function test_question_manually_graded() {
 729          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 730  
 731          $params = array(
 732              'objectid' => 1,
 733              'courseid' => $quizobj->get_courseid(),
 734              'context' => \context_module::instance($quizobj->get_cmid()),
 735              'other' => array(
 736                  'quizid' => $quizobj->get_quizid(),
 737                  'attemptid' => 2,
 738                  'slot' => 3
 739              )
 740          );
 741          $event = \mod_quiz\event\question_manually_graded::create($params);
 742  
 743          // Trigger and capture the event.
 744          $sink = $this->redirectEvents();
 745          $event->trigger();
 746          $events = $sink->get_events();
 747          $event = reset($events);
 748  
 749          // Check that the event data is valid.
 750          $this->assertInstanceOf('\mod_quiz\event\question_manually_graded', $event);
 751          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 752          $expected = array($quizobj->get_courseid(), 'quiz', 'manualgrade', 'comment.php?attempt=2&slot=3',
 753              $quizobj->get_quizid(), $quizobj->get_cmid());
 754          $this->assertEventLegacyLogData($expected, $event);
 755          $this->assertEventContextNotUsed($event);
 756      }
 757  
 758      /**
 759       * Test the attempt regraded event.
 760       *
 761       * There is no external API for regrading attempts, so the unit test will simply
 762       * create and trigger the event and ensure the event data is returned as expected.
 763       */
 764      public function test_attempt_regraded() {
 765        $this->resetAfterTest();
 766  
 767        $this->setAdminUser();
 768        $course = $this->getDataGenerator()->create_course();
 769        $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 770  
 771        $params = array(
 772          'objectid' => 1,
 773          'relateduserid' => 2,
 774          'courseid' => $course->id,
 775          'context' => \context_module::instance($quiz->cmid),
 776          'other' => array(
 777            'quizid' => $quiz->id
 778          )
 779        );
 780        $event = \mod_quiz\event\attempt_regraded::create($params);
 781  
 782        // Trigger and capture the event.
 783        $sink = $this->redirectEvents();
 784        $event->trigger();
 785        $events = $sink->get_events();
 786        $event = reset($events);
 787  
 788        // Check that the event data is valid.
 789        $this->assertInstanceOf('\mod_quiz\event\attempt_regraded', $event);
 790        $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 791        $this->assertEventContextNotUsed($event);
 792      }
 793  }