Search moodle.org's
Developer Documentation

See Release Notes

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