Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 311 and 401] [Versions 401 and 402] [Versions 401 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  use context_module;
  31  
  32  defined('MOODLE_INTERNAL') || die();
  33  
  34  global $CFG;
  35  require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
  36  
  37  /**
  38   * Unit tests for quiz events.
  39   *
  40   * @package    mod_quiz
  41   * @category   phpunit
  42   * @copyright  2013 Adrian Greeve
  43   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  44   */
  45  class events_test extends \advanced_testcase {
  46  
  47      /**
  48       * Setup a quiz.
  49       *
  50       * @return quiz the generated quiz.
  51       */
  52      protected function prepare_quiz() {
  53  
  54          $this->resetAfterTest(true);
  55  
  56          // Create a course
  57          $course = $this->getDataGenerator()->create_course();
  58  
  59          // Make a quiz.
  60          $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
  61  
  62          $quiz = $quizgenerator->create_instance(array('course' => $course->id, 'questionsperpage' => 0,
  63                  'grade' => 100.0, 'sumgrades' => 2));
  64  
  65          $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id);
  66  
  67          // Create a couple of questions.
  68          $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
  69  
  70          $cat = $questiongenerator->create_question_category();
  71          $saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
  72          $numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
  73  
  74          // Add them to the quiz.
  75          quiz_add_quiz_question($saq->id, $quiz);
  76          quiz_add_quiz_question($numq->id, $quiz);
  77  
  78          // Make a user to do the quiz.
  79          $user1 = $this->getDataGenerator()->create_user();
  80          $this->setUser($user1);
  81  
  82          return quiz::create($quiz->id, $user1->id);
  83      }
  84  
  85      /**
  86       * Setup a quiz attempt at the quiz created by {@link prepare_quiz()}.
  87       *
  88       * @param quiz $quizobj the generated quiz.
  89       * @param bool $ispreview Make the attempt a preview attempt when true.
  90       * @return array with three elements, array($quizobj, $quba, $attempt)
  91       */
  92      protected function prepare_quiz_attempt($quizobj, $ispreview = false) {
  93          // Start the attempt.
  94          $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
  95          $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
  96  
  97          $timenow = time();
  98          $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, $ispreview);
  99          quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
 100          quiz_attempt_save_started($quizobj, $quba, $attempt);
 101  
 102          return array($quizobj, $quba, $attempt);
 103      }
 104  
 105      /**
 106       * Setup some convenience test data with a single attempt.
 107       *
 108       * @param bool $ispreview Make the attempt a preview attempt when true.
 109       * @return array with three elements, array($quizobj, $quba, $attempt)
 110       */
 111      protected function prepare_quiz_data($ispreview = false) {
 112          $quizobj = $this->prepare_quiz();
 113          return $this->prepare_quiz_attempt($quizobj, $ispreview);
 114      }
 115  
 116      public function test_attempt_submitted() {
 117  
 118          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 119          $attemptobj = quiz_attempt::create($attempt->id);
 120  
 121          // Catch the event.
 122          $sink = $this->redirectEvents();
 123  
 124          $timefinish = time();
 125          $attemptobj->process_finish($timefinish, false);
 126          $events = $sink->get_events();
 127          $sink->close();
 128  
 129          // Validate the event.
 130          $this->assertCount(3, $events);
 131          $event = $events[2];
 132          $this->assertInstanceOf('\mod_quiz\event\attempt_submitted', $event);
 133          $this->assertEquals('quiz_attempts', $event->objecttable);
 134          $this->assertEquals($quizobj->get_context(), $event->get_context());
 135          $this->assertEquals($attempt->userid, $event->relateduserid);
 136          $this->assertEquals(null, $event->other['submitterid']); // Should be the user, but PHP Unit complains...
 137          $this->assertEquals('quiz_attempt_submitted', $event->get_legacy_eventname());
 138          $legacydata = new \stdClass();
 139          $legacydata->component = 'mod_quiz';
 140          $legacydata->attemptid = (string) $attempt->id;
 141          $legacydata->timestamp = $timefinish;
 142          $legacydata->userid = $attempt->userid;
 143          $legacydata->cmid = $quizobj->get_cmid();
 144          $legacydata->courseid = $quizobj->get_courseid();
 145          $legacydata->quizid = $quizobj->get_quizid();
 146          // Submitterid should be the user, but as we are in PHP Unit, CLI_SCRIPT is set to true which sets null in submitterid.
 147          $legacydata->submitterid = null;
 148          $legacydata->timefinish = $timefinish;
 149          $this->assertEventLegacyData($legacydata, $event);
 150          $this->assertEventContextNotUsed($event);
 151      }
 152  
 153      public function test_attempt_becameoverdue() {
 154  
 155          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 156          $attemptobj = quiz_attempt::create($attempt->id);
 157  
 158          // Catch the event.
 159          $sink = $this->redirectEvents();
 160          $timefinish = time();
 161          $attemptobj->process_going_overdue($timefinish, false);
 162          $events = $sink->get_events();
 163          $sink->close();
 164  
 165          $this->assertCount(1, $events);
 166          $event = $events[0];
 167          $this->assertInstanceOf('\mod_quiz\event\attempt_becameoverdue', $event);
 168          $this->assertEquals('quiz_attempts', $event->objecttable);
 169          $this->assertEquals($quizobj->get_context(), $event->get_context());
 170          $this->assertEquals($attempt->userid, $event->relateduserid);
 171          $this->assertNotEmpty($event->get_description());
 172          // Submitterid should be the user, but as we are in PHP Unit, CLI_SCRIPT is set to true which sets null in submitterid.
 173          $this->assertEquals(null, $event->other['submitterid']);
 174          $this->assertEquals('quiz_attempt_overdue', $event->get_legacy_eventname());
 175          $legacydata = new \stdClass();
 176          $legacydata->component = 'mod_quiz';
 177          $legacydata->attemptid = (string) $attempt->id;
 178          $legacydata->timestamp = $timefinish;
 179          $legacydata->userid = $attempt->userid;
 180          $legacydata->cmid = $quizobj->get_cmid();
 181          $legacydata->courseid = $quizobj->get_courseid();
 182          $legacydata->quizid = $quizobj->get_quizid();
 183          $legacydata->submitterid = null; // Should be the user, but PHP Unit complains...
 184          $this->assertEventLegacyData($legacydata, $event);
 185          $this->assertEventContextNotUsed($event);
 186      }
 187  
 188      public function test_attempt_abandoned() {
 189  
 190          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 191          $attemptobj = quiz_attempt::create($attempt->id);
 192  
 193          // Catch the event.
 194          $sink = $this->redirectEvents();
 195          $timefinish = time();
 196          $attemptobj->process_abandon($timefinish, false);
 197          $events = $sink->get_events();
 198          $sink->close();
 199  
 200          $this->assertCount(1, $events);
 201          $event = $events[0];
 202          $this->assertInstanceOf('\mod_quiz\event\attempt_abandoned', $event);
 203          $this->assertEquals('quiz_attempts', $event->objecttable);
 204          $this->assertEquals($quizobj->get_context(), $event->get_context());
 205          $this->assertEquals($attempt->userid, $event->relateduserid);
 206          // Submitterid should be the user, but as we are in PHP Unit, CLI_SCRIPT is set to true which sets null in submitterid.
 207          $this->assertEquals(null, $event->other['submitterid']);
 208          $this->assertEquals('quiz_attempt_abandoned', $event->get_legacy_eventname());
 209          $legacydata = new \stdClass();
 210          $legacydata->component = 'mod_quiz';
 211          $legacydata->attemptid = (string) $attempt->id;
 212          $legacydata->timestamp = $timefinish;
 213          $legacydata->userid = $attempt->userid;
 214          $legacydata->cmid = $quizobj->get_cmid();
 215          $legacydata->courseid = $quizobj->get_courseid();
 216          $legacydata->quizid = $quizobj->get_quizid();
 217          $legacydata->submitterid = null; // Should be the user, but PHP Unit complains...
 218          $this->assertEventLegacyData($legacydata, $event);
 219          $this->assertEventContextNotUsed($event);
 220      }
 221  
 222      public function test_attempt_started() {
 223          $quizobj = $this->prepare_quiz();
 224  
 225          $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
 226          $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
 227  
 228          $timenow = time();
 229          $attempt = quiz_create_attempt($quizobj, 1, false, $timenow);
 230          quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
 231  
 232          // Trigger and capture the event.
 233          $sink = $this->redirectEvents();
 234          quiz_attempt_save_started($quizobj, $quba, $attempt);
 235          $events = $sink->get_events();
 236          $event = reset($events);
 237  
 238          // Check that the event data is valid.
 239          $this->assertInstanceOf('\mod_quiz\event\attempt_started', $event);
 240          $this->assertEquals('quiz_attempts', $event->objecttable);
 241          $this->assertEquals($attempt->id, $event->objectid);
 242          $this->assertEquals($attempt->userid, $event->relateduserid);
 243          $this->assertEquals($quizobj->get_context(), $event->get_context());
 244          $this->assertEquals('quiz_attempt_started', $event->get_legacy_eventname());
 245          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 246          // Check legacy log data.
 247          $expected = array($quizobj->get_courseid(), 'quiz', 'attempt', 'review.php?attempt=' . $attempt->id,
 248              $quizobj->get_quizid(), $quizobj->get_cmid());
 249          $this->assertEventLegacyLogData($expected, $event);
 250          // Check legacy event data.
 251          $legacydata = new \stdClass();
 252          $legacydata->component = 'mod_quiz';
 253          $legacydata->attemptid = $attempt->id;
 254          $legacydata->timestart = $attempt->timestart;
 255          $legacydata->timestamp = $attempt->timestart;
 256          $legacydata->userid = $attempt->userid;
 257          $legacydata->quizid = $quizobj->get_quizid();
 258          $legacydata->cmid = $quizobj->get_cmid();
 259          $legacydata->courseid = $quizobj->get_courseid();
 260          $this->assertEventLegacyData($legacydata, $event);
 261          $this->assertEventContextNotUsed($event);
 262      }
 263  
 264      /**
 265       * Test the attempt question restarted event.
 266       *
 267       * There is no external API for replacing a question, so the unit test will simply
 268       * create and trigger the event and ensure the event data is returned as expected.
 269       */
 270      public function test_attempt_question_restarted() {
 271          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 272  
 273          $params = [
 274              'objectid' => 1,
 275              'relateduserid' => 2,
 276              'courseid' => $quizobj->get_courseid(),
 277              'context' => \context_module::instance($quizobj->get_cmid()),
 278              'other' => [
 279                  'quizid' => $quizobj->get_quizid(),
 280                  'page' => 2,
 281                  'slot' => 3,
 282                  'newquestionid' => 2
 283              ]
 284          ];
 285          $event = \mod_quiz\event\attempt_question_restarted::create($params);
 286  
 287          // Trigger and capture the event.
 288          $sink = $this->redirectEvents();
 289          $event->trigger();
 290          $events = $sink->get_events();
 291          $event = reset($events);
 292  
 293          // Check that the event data is valid.
 294          $this->assertInstanceOf('\mod_quiz\event\attempt_question_restarted', $event);
 295          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 296          $this->assertEventContextNotUsed($event);
 297      }
 298  
 299      /**
 300       * Test the attempt updated event.
 301       *
 302       * There is no external API for updating an attempt, so the unit test will simply
 303       * create and trigger the event and ensure the event data is returned as expected.
 304       */
 305      public function test_attempt_updated() {
 306          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 307  
 308          $params = [
 309              'objectid' => 1,
 310              'relateduserid' => 2,
 311              'courseid' => $quizobj->get_courseid(),
 312              'context' => \context_module::instance($quizobj->get_cmid()),
 313              'other' => [
 314                  'quizid' => $quizobj->get_quizid(),
 315                  'page' => 0
 316              ]
 317          ];
 318          $event = \mod_quiz\event\attempt_updated::create($params);
 319  
 320          // Trigger and capture the event.
 321          $sink = $this->redirectEvents();
 322          $event->trigger();
 323          $events = $sink->get_events();
 324          $event = reset($events);
 325  
 326          // Check that the event data is valid.
 327          $this->assertInstanceOf('\mod_quiz\event\attempt_updated', $event);
 328          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 329          $this->assertEventContextNotUsed($event);
 330      }
 331  
 332      /**
 333       * Test the attempt auto-saved event.
 334       *
 335       * There is no external API for auto-saving an attempt, so the unit test will simply
 336       * create and trigger the event and ensure the event data is returned as expected.
 337       */
 338      public function test_attempt_autosaved() {
 339          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 340  
 341          $params = [
 342              'objectid' => 1,
 343              'relateduserid' => 2,
 344              'courseid' => $quizobj->get_courseid(),
 345              'context' => \context_module::instance($quizobj->get_cmid()),
 346              'other' => [
 347                  'quizid' => $quizobj->get_quizid(),
 348                  'page' => 0
 349              ]
 350          ];
 351  
 352          $event = \mod_quiz\event\attempt_autosaved::create($params);
 353  
 354          // Trigger and capture the event.
 355          $sink = $this->redirectEvents();
 356          $event->trigger();
 357          $events = $sink->get_events();
 358          $event = reset($events);
 359  
 360          // Check that the event data is valid.
 361          $this->assertInstanceOf('\mod_quiz\event\attempt_autosaved', $event);
 362          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 363          $this->assertEventContextNotUsed($event);
 364      }
 365  
 366      /**
 367       * Test the edit page viewed event.
 368       *
 369       * There is no external API for updating a quiz, so the unit test will simply
 370       * create and trigger the event and ensure the event data is returned as expected.
 371       */
 372      public function test_edit_page_viewed() {
 373          $this->resetAfterTest();
 374  
 375          $this->setAdminUser();
 376          $course = $this->getDataGenerator()->create_course();
 377          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 378  
 379          $params = array(
 380              'courseid' => $course->id,
 381              'context' => \context_module::instance($quiz->cmid),
 382              'other' => array(
 383                  'quizid' => $quiz->id
 384              )
 385          );
 386          $event = \mod_quiz\event\edit_page_viewed::create($params);
 387  
 388          // Trigger and capture the event.
 389          $sink = $this->redirectEvents();
 390          $event->trigger();
 391          $events = $sink->get_events();
 392          $event = reset($events);
 393  
 394          // Check that the event data is valid.
 395          $this->assertInstanceOf('\mod_quiz\event\edit_page_viewed', $event);
 396          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 397          $expected = array($course->id, 'quiz', 'editquestions', 'view.php?id=' . $quiz->cmid, $quiz->id, $quiz->cmid);
 398          $this->assertEventLegacyLogData($expected, $event);
 399          $this->assertEventContextNotUsed($event);
 400      }
 401  
 402      /**
 403       * Test the attempt deleted event.
 404       */
 405      public function test_attempt_deleted() {
 406          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 407  
 408          // Trigger and capture the event.
 409          $sink = $this->redirectEvents();
 410          quiz_delete_attempt($attempt, $quizobj->get_quiz());
 411          $events = $sink->get_events();
 412          $event = reset($events);
 413  
 414          // Check that the event data is valid.
 415          $this->assertInstanceOf('\mod_quiz\event\attempt_deleted', $event);
 416          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 417          $expected = array($quizobj->get_courseid(), 'quiz', 'delete attempt', 'report.php?id=' . $quizobj->get_cmid(),
 418              $attempt->id, $quizobj->get_cmid());
 419          $this->assertEventLegacyLogData($expected, $event);
 420          $this->assertEventContextNotUsed($event);
 421      }
 422  
 423      /**
 424       * Test that preview attempt deletions are not logged.
 425       */
 426      public function test_preview_attempt_deleted() {
 427          // Create quiz with preview attempt.
 428          list($quizobj, $quba, $previewattempt) = $this->prepare_quiz_data(true);
 429  
 430          // Delete a preview attempt, capturing events.
 431          $sink = $this->redirectEvents();
 432          quiz_delete_attempt($previewattempt, $quizobj->get_quiz());
 433  
 434          // Verify that no events were generated.
 435          $this->assertEmpty($sink->get_events());
 436      }
 437  
 438      /**
 439       * Test the report viewed event.
 440       *
 441       * There is no external API for viewing reports, so the unit test will simply
 442       * create and trigger the event and ensure the event data is returned as expected.
 443       */
 444      public function test_report_viewed() {
 445          $this->resetAfterTest();
 446  
 447          $this->setAdminUser();
 448          $course = $this->getDataGenerator()->create_course();
 449          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 450  
 451          $params = array(
 452              'context' => $context = \context_module::instance($quiz->cmid),
 453              'other' => array(
 454                  'quizid' => $quiz->id,
 455                  'reportname' => 'overview'
 456              )
 457          );
 458          $event = \mod_quiz\event\report_viewed::create($params);
 459  
 460          // Trigger and capture the event.
 461          $sink = $this->redirectEvents();
 462          $event->trigger();
 463          $events = $sink->get_events();
 464          $event = reset($events);
 465  
 466          // Check that the event data is valid.
 467          $this->assertInstanceOf('\mod_quiz\event\report_viewed', $event);
 468          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 469          $expected = array($course->id, 'quiz', 'report', 'report.php?id=' . $quiz->cmid . '&mode=overview',
 470              $quiz->id, $quiz->cmid);
 471          $this->assertEventLegacyLogData($expected, $event);
 472          $this->assertEventContextNotUsed($event);
 473      }
 474  
 475      /**
 476       * Test the attempt reviewed event.
 477       *
 478       * There is no external API for reviewing attempts, so the unit test will simply
 479       * create and trigger the event and ensure the event data is returned as expected.
 480       */
 481      public function test_attempt_reviewed() {
 482          $this->resetAfterTest();
 483  
 484          $this->setAdminUser();
 485          $course = $this->getDataGenerator()->create_course();
 486          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 487  
 488          $params = array(
 489              'objectid' => 1,
 490              'relateduserid' => 2,
 491              'courseid' => $course->id,
 492              'context' => \context_module::instance($quiz->cmid),
 493              'other' => array(
 494                  'quizid' => $quiz->id
 495              )
 496          );
 497          $event = \mod_quiz\event\attempt_reviewed::create($params);
 498  
 499          // Trigger and capture the event.
 500          $sink = $this->redirectEvents();
 501          $event->trigger();
 502          $events = $sink->get_events();
 503          $event = reset($events);
 504  
 505          // Check that the event data is valid.
 506          $this->assertInstanceOf('\mod_quiz\event\attempt_reviewed', $event);
 507          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 508          $expected = array($course->id, 'quiz', 'review', 'review.php?attempt=1', $quiz->id, $quiz->cmid);
 509          $this->assertEventLegacyLogData($expected, $event);
 510          $this->assertEventContextNotUsed($event);
 511      }
 512  
 513      /**
 514       * Test the attempt summary viewed event.
 515       *
 516       * There is no external API for viewing the attempt summary, 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_attempt_summary_viewed() {
 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              'courseid' => $course->id,
 530              'context' => \context_module::instance($quiz->cmid),
 531              'other' => array(
 532                  'quizid' => $quiz->id
 533              )
 534          );
 535          $event = \mod_quiz\event\attempt_summary_viewed::create($params);
 536  
 537          // Trigger and capture the event.
 538          $sink = $this->redirectEvents();
 539          $event->trigger();
 540          $events = $sink->get_events();
 541          $event = reset($events);
 542  
 543          // Check that the event data is valid.
 544          $this->assertInstanceOf('\mod_quiz\event\attempt_summary_viewed', $event);
 545          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 546          $expected = array($course->id, 'quiz', 'view summary', 'summary.php?attempt=1', $quiz->id, $quiz->cmid);
 547          $this->assertEventLegacyLogData($expected, $event);
 548          $this->assertEventContextNotUsed($event);
 549      }
 550  
 551      /**
 552       * Test the user override created event.
 553       *
 554       * There is no external API for creating a user override, so the unit test will simply
 555       * create and trigger the event and ensure the event data is returned as expected.
 556       */
 557      public function test_user_override_created() {
 558          $this->resetAfterTest();
 559  
 560          $this->setAdminUser();
 561          $course = $this->getDataGenerator()->create_course();
 562          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 563  
 564          $params = array(
 565              'objectid' => 1,
 566              'relateduserid' => 2,
 567              'context' => \context_module::instance($quiz->cmid),
 568              'other' => array(
 569                  'quizid' => $quiz->id
 570              )
 571          );
 572          $event = \mod_quiz\event\user_override_created::create($params);
 573  
 574          // Trigger and capture the event.
 575          $sink = $this->redirectEvents();
 576          $event->trigger();
 577          $events = $sink->get_events();
 578          $event = reset($events);
 579  
 580          // Check that the event data is valid.
 581          $this->assertInstanceOf('\mod_quiz\event\user_override_created', $event);
 582          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 583          $this->assertEventContextNotUsed($event);
 584      }
 585  
 586      /**
 587       * Test the group override created event.
 588       *
 589       * There is no external API for creating a group override, so the unit test will simply
 590       * create and trigger the event and ensure the event data is returned as expected.
 591       */
 592      public function test_group_override_created() {
 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          $params = array(
 600              'objectid' => 1,
 601              'context' => \context_module::instance($quiz->cmid),
 602              'other' => array(
 603                  'quizid' => $quiz->id,
 604                  'groupid' => 2
 605              )
 606          );
 607          $event = \mod_quiz\event\group_override_created::create($params);
 608  
 609          // Trigger and capture the event.
 610          $sink = $this->redirectEvents();
 611          $event->trigger();
 612          $events = $sink->get_events();
 613          $event = reset($events);
 614  
 615          // Check that the event data is valid.
 616          $this->assertInstanceOf('\mod_quiz\event\group_override_created', $event);
 617          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 618          $this->assertEventContextNotUsed($event);
 619      }
 620  
 621      /**
 622       * Test the user override updated event.
 623       *
 624       * There is no external API for updating a user override, so the unit test will simply
 625       * create and trigger the event and ensure the event data is returned as expected.
 626       */
 627      public function test_user_override_updated() {
 628          $this->resetAfterTest();
 629  
 630          $this->setAdminUser();
 631          $course = $this->getDataGenerator()->create_course();
 632          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 633  
 634          $params = array(
 635              'objectid' => 1,
 636              'relateduserid' => 2,
 637              'context' => \context_module::instance($quiz->cmid),
 638              'other' => array(
 639                  'quizid' => $quiz->id
 640              )
 641          );
 642          $event = \mod_quiz\event\user_override_updated::create($params);
 643  
 644          // Trigger and capture the event.
 645          $sink = $this->redirectEvents();
 646          $event->trigger();
 647          $events = $sink->get_events();
 648          $event = reset($events);
 649  
 650          // Check that the event data is valid.
 651          $this->assertInstanceOf('\mod_quiz\event\user_override_updated', $event);
 652          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 653          $expected = array($course->id, 'quiz', 'edit override', 'overrideedit.php?id=1', $quiz->id, $quiz->cmid);
 654          $this->assertEventLegacyLogData($expected, $event);
 655          $this->assertEventContextNotUsed($event);
 656      }
 657  
 658      /**
 659       * Test the group override updated event.
 660       *
 661       * There is no external API for updating a group override, so the unit test will simply
 662       * create and trigger the event and ensure the event data is returned as expected.
 663       */
 664      public function test_group_override_updated() {
 665          $this->resetAfterTest();
 666  
 667          $this->setAdminUser();
 668          $course = $this->getDataGenerator()->create_course();
 669          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 670  
 671          $params = array(
 672              'objectid' => 1,
 673              'context' => \context_module::instance($quiz->cmid),
 674              'other' => array(
 675                  'quizid' => $quiz->id,
 676                  'groupid' => 2
 677              )
 678          );
 679          $event = \mod_quiz\event\group_override_updated::create($params);
 680  
 681          // Trigger and capture the event.
 682          $sink = $this->redirectEvents();
 683          $event->trigger();
 684          $events = $sink->get_events();
 685          $event = reset($events);
 686  
 687          // Check that the event data is valid.
 688          $this->assertInstanceOf('\mod_quiz\event\group_override_updated', $event);
 689          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 690          $expected = array($course->id, 'quiz', 'edit override', 'overrideedit.php?id=1', $quiz->id, $quiz->cmid);
 691          $this->assertEventLegacyLogData($expected, $event);
 692          $this->assertEventContextNotUsed($event);
 693      }
 694  
 695      /**
 696       * Test the user override deleted event.
 697       */
 698      public function test_user_override_deleted() {
 699          global $DB;
 700  
 701          $this->resetAfterTest();
 702  
 703          $this->setAdminUser();
 704          $course = $this->getDataGenerator()->create_course();
 705          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 706  
 707          // Create an override.
 708          $override = new \stdClass();
 709          $override->quiz = $quiz->id;
 710          $override->userid = 2;
 711          $override->id = $DB->insert_record('quiz_overrides', $override);
 712  
 713          // Trigger and capture the event.
 714          $sink = $this->redirectEvents();
 715          quiz_delete_override($quiz, $override->id);
 716          $events = $sink->get_events();
 717          $event = reset($events);
 718  
 719          // Check that the event data is valid.
 720          $this->assertInstanceOf('\mod_quiz\event\user_override_deleted', $event);
 721          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 722          $expected = array($course->id, 'quiz', 'delete override', 'overrides.php?cmid=' . $quiz->cmid, $quiz->id, $quiz->cmid);
 723          $this->assertEventLegacyLogData($expected, $event);
 724          $this->assertEventContextNotUsed($event);
 725      }
 726  
 727      /**
 728       * Test the group override deleted event.
 729       */
 730      public function test_group_override_deleted() {
 731          global $DB;
 732  
 733          $this->resetAfterTest();
 734  
 735          $this->setAdminUser();
 736          $course = $this->getDataGenerator()->create_course();
 737          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 738  
 739          // Create an override.
 740          $override = new \stdClass();
 741          $override->quiz = $quiz->id;
 742          $override->groupid = 2;
 743          $override->id = $DB->insert_record('quiz_overrides', $override);
 744  
 745          // Trigger and capture the event.
 746          $sink = $this->redirectEvents();
 747          quiz_delete_override($quiz, $override->id);
 748          $events = $sink->get_events();
 749          $event = reset($events);
 750  
 751          // Check that the event data is valid.
 752          $this->assertInstanceOf('\mod_quiz\event\group_override_deleted', $event);
 753          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 754          $expected = array($course->id, 'quiz', 'delete override', 'overrides.php?cmid=' . $quiz->cmid, $quiz->id, $quiz->cmid);
 755          $this->assertEventLegacyLogData($expected, $event);
 756          $this->assertEventContextNotUsed($event);
 757      }
 758  
 759      /**
 760       * Test the attempt viewed event.
 761       *
 762       * There is no external API for continuing an attempt, so the unit test will simply
 763       * create and trigger the event and ensure the event data is returned as expected.
 764       */
 765      public function test_attempt_viewed() {
 766          $this->resetAfterTest();
 767  
 768          $this->setAdminUser();
 769          $course = $this->getDataGenerator()->create_course();
 770          $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 771  
 772          $params = array(
 773              'objectid' => 1,
 774              'relateduserid' => 2,
 775              'courseid' => $course->id,
 776              'context' => \context_module::instance($quiz->cmid),
 777              'other' => array(
 778                  'quizid' => $quiz->id,
 779                  'page' => 0
 780              )
 781          );
 782          $event = \mod_quiz\event\attempt_viewed::create($params);
 783  
 784          // Trigger and capture the event.
 785          $sink = $this->redirectEvents();
 786          $event->trigger();
 787          $events = $sink->get_events();
 788          $event = reset($events);
 789  
 790          // Check that the event data is valid.
 791          $this->assertInstanceOf('\mod_quiz\event\attempt_viewed', $event);
 792          $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 793          $expected = array($course->id, 'quiz', 'continue attempt', 'review.php?attempt=1', $quiz->id, $quiz->cmid);
 794          $this->assertEventLegacyLogData($expected, $event);
 795          $this->assertEventContextNotUsed($event);
 796      }
 797  
 798      /**
 799       * Test the attempt previewed event.
 800       */
 801      public function test_attempt_preview_started() {
 802          $quizobj = $this->prepare_quiz();
 803  
 804          $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
 805          $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
 806  
 807          $timenow = time();
 808          $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, true);
 809          quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
 810  
 811          // Trigger and capture the event.
 812          $sink = $this->redirectEvents();
 813          quiz_attempt_save_started($quizobj, $quba, $attempt);
 814          $events = $sink->get_events();
 815          $event = reset($events);
 816  
 817          // Check that the event data is valid.
 818          $this->assertInstanceOf('\mod_quiz\event\attempt_preview_started', $event);
 819          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 820          $expected = array($quizobj->get_courseid(), 'quiz', 'preview', 'view.php?id=' . $quizobj->get_cmid(),
 821              $quizobj->get_quizid(), $quizobj->get_cmid());
 822          $this->assertEventLegacyLogData($expected, $event);
 823          $this->assertEventContextNotUsed($event);
 824      }
 825  
 826      /**
 827       * Test the question manually graded event.
 828       *
 829       * There is no external API for manually grading a question, so the unit test will simply
 830       * create and trigger the event and ensure the event data is returned as expected.
 831       */
 832      public function test_question_manually_graded() {
 833          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 834  
 835          $params = array(
 836              'objectid' => 1,
 837              'courseid' => $quizobj->get_courseid(),
 838              'context' => \context_module::instance($quizobj->get_cmid()),
 839              'other' => array(
 840                  'quizid' => $quizobj->get_quizid(),
 841                  'attemptid' => 2,
 842                  'slot' => 3
 843              )
 844          );
 845          $event = \mod_quiz\event\question_manually_graded::create($params);
 846  
 847          // Trigger and capture the event.
 848          $sink = $this->redirectEvents();
 849          $event->trigger();
 850          $events = $sink->get_events();
 851          $event = reset($events);
 852  
 853          // Check that the event data is valid.
 854          $this->assertInstanceOf('\mod_quiz\event\question_manually_graded', $event);
 855          $this->assertEquals(\context_module::instance($quizobj->get_cmid()), $event->get_context());
 856          $expected = array($quizobj->get_courseid(), 'quiz', 'manualgrade', 'comment.php?attempt=2&slot=3',
 857              $quizobj->get_quizid(), $quizobj->get_cmid());
 858          $this->assertEventLegacyLogData($expected, $event);
 859          $this->assertEventContextNotUsed($event);
 860      }
 861  
 862      /**
 863       * Test the attempt regraded event.
 864       *
 865       * There is no external API for regrading attempts, so the unit test will simply
 866       * create and trigger the event and ensure the event data is returned as expected.
 867       */
 868      public function test_attempt_regraded() {
 869        $this->resetAfterTest();
 870  
 871        $this->setAdminUser();
 872        $course = $this->getDataGenerator()->create_course();
 873        $quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
 874  
 875        $params = array(
 876          'objectid' => 1,
 877          'relateduserid' => 2,
 878          'courseid' => $course->id,
 879          'context' => \context_module::instance($quiz->cmid),
 880          'other' => array(
 881            'quizid' => $quiz->id
 882          )
 883        );
 884        $event = \mod_quiz\event\attempt_regraded::create($params);
 885  
 886        // Trigger and capture the event.
 887        $sink = $this->redirectEvents();
 888        $event->trigger();
 889        $events = $sink->get_events();
 890        $event = reset($events);
 891  
 892        // Check that the event data is valid.
 893        $this->assertInstanceOf('\mod_quiz\event\attempt_regraded', $event);
 894        $this->assertEquals(\context_module::instance($quiz->cmid), $event->get_context());
 895        $this->assertEventContextNotUsed($event);
 896      }
 897  
 898      /**
 899       * Test the attempt notify manual graded event.
 900       * There is no external API for notification email when manual grading of user's attempt is completed,
 901       * so the unit test will simply create and trigger the event and ensure the event data is returned as expected.
 902       */
 903      public function test_attempt_manual_grading_completed() {
 904          $this->resetAfterTest();
 905          list($quizobj, $quba, $attempt) = $this->prepare_quiz_data();
 906          $attemptobj = quiz_attempt::create($attempt->id);
 907  
 908          $params = [
 909              'objectid' => $attemptobj->get_attemptid(),
 910              'relateduserid' => $attemptobj->get_userid(),
 911              'courseid' => $attemptobj->get_course()->id,
 912              'context' => \context_module::instance($attemptobj->get_cmid()),
 913              'other' => [
 914                  'quizid' => $attemptobj->get_quizid()
 915              ]
 916          ];
 917          $event = \mod_quiz\event\attempt_manual_grading_completed::create($params);
 918  
 919          // Catch the event.
 920          $sink = $this->redirectEvents();
 921          $event->trigger();
 922          $events = $sink->get_events();
 923          $sink->close();
 924  
 925          // Validate the event.
 926          $this->assertCount(1, $events);
 927          $event = reset($events);
 928          $this->assertInstanceOf('\mod_quiz\event\attempt_manual_grading_completed', $event);
 929          $this->assertEquals('quiz_attempts', $event->objecttable);
 930          $this->assertEquals($quizobj->get_context(), $event->get_context());
 931          $this->assertEquals($attempt->userid, $event->relateduserid);
 932          $this->assertNotEmpty($event->get_description());
 933          $this->assertEventContextNotUsed($event);
 934      }
 935  
 936      /**
 937       * Test the page break created event.
 938       *
 939       * There is no external API for creating page break, so the unit test will simply
 940       * create and trigger the event and ensure the event data is returned as expected.
 941       */
 942      public function test_page_break_created() {
 943          $quizobj = $this->prepare_quiz();
 944  
 945          $params = [
 946              'objectid' => 1,
 947              'context' => context_module::instance($quizobj->get_cmid()),
 948              'other' => [
 949                  'quizid' => $quizobj->get_quizid(),
 950                  'slotnumber' => 3,
 951              ]
 952          ];
 953          $event = \mod_quiz\event\page_break_created::create($params);
 954  
 955          // Trigger and capture the event.
 956          $sink = $this->redirectEvents();
 957          $event->trigger();
 958          $events = $sink->get_events();
 959          $event = reset($events);
 960  
 961          // Check that the event data is valid.
 962          $this->assertInstanceOf('\mod_quiz\event\page_break_created', $event);
 963          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
 964          $this->assertEventContextNotUsed($event);
 965      }
 966  
 967      /**
 968       * Test the page break deleted event.
 969       *
 970       * There is no external API for deleting page break, so the unit test will simply
 971       * create and trigger the event and ensure the event data is returned as expected.
 972       */
 973      public function test_page_deleted_created() {
 974          $quizobj = $this->prepare_quiz();
 975  
 976          $params = [
 977              'objectid' => 1,
 978              'context' => context_module::instance($quizobj->get_cmid()),
 979              'other' => [
 980                  'quizid' => $quizobj->get_quizid(),
 981                  'slotnumber' => 3,
 982              ]
 983          ];
 984          $event = \mod_quiz\event\page_break_deleted::create($params);
 985  
 986          // Trigger and capture the event.
 987          $sink = $this->redirectEvents();
 988          $event->trigger();
 989          $events = $sink->get_events();
 990          $event = reset($events);
 991  
 992          // Check that the event data is valid.
 993          $this->assertInstanceOf('\mod_quiz\event\page_break_deleted', $event);
 994          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
 995          $this->assertEventContextNotUsed($event);
 996      }
 997  
 998      /**
 999       * Test the quiz grade updated event.
1000       *
1001       * There is no external API for updating quiz grade, so the unit test will simply
1002       * create and trigger the event and ensure the event data is returned as expected.
1003       */
1004      public function test_quiz_grade_updated() {
1005          $quizobj = $this->prepare_quiz();
1006  
1007          $params = [
1008              'objectid' => $quizobj->get_quizid(),
1009              'context' => context_module::instance($quizobj->get_cmid()),
1010              'other' => [
1011                  'oldgrade' => 1,
1012                  'newgrade' => 3,
1013              ]
1014          ];
1015          $event = \mod_quiz\event\quiz_grade_updated::create($params);
1016  
1017          // Trigger and capture the event.
1018          $sink = $this->redirectEvents();
1019          $event->trigger();
1020          $events = $sink->get_events();
1021          $event = reset($events);
1022  
1023          // Check that the event data is valid.
1024          $this->assertInstanceOf('\mod_quiz\event\quiz_grade_updated', $event);
1025          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1026          $this->assertEventContextNotUsed($event);
1027      }
1028  
1029      /**
1030       * Test the quiz re-paginated event.
1031       *
1032       * There is no external API for re-paginating quiz, so the unit test will simply
1033       * create and trigger the event and ensure the event data is returned as expected.
1034       */
1035      public function test_quiz_repaginated() {
1036          $quizobj = $this->prepare_quiz();
1037  
1038          $params = [
1039              'objectid' => $quizobj->get_quizid(),
1040              'context' => context_module::instance($quizobj->get_cmid()),
1041              'other' => [
1042                  'slotsperpage' => 3,
1043              ]
1044          ];
1045          $event = \mod_quiz\event\quiz_repaginated::create($params);
1046  
1047          // Trigger and capture the event.
1048          $sink = $this->redirectEvents();
1049          $event->trigger();
1050          $events = $sink->get_events();
1051          $event = reset($events);
1052  
1053          // Check that the event data is valid.
1054          $this->assertInstanceOf('\mod_quiz\event\quiz_repaginated', $event);
1055          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1056          $this->assertEventContextNotUsed($event);
1057      }
1058  
1059      /**
1060       * Test the section break created event.
1061       *
1062       * There is no external API for creating section break, so the unit test will simply
1063       * create and trigger the event and ensure the event data is returned as expected.
1064       */
1065      public function test_section_break_created() {
1066          $quizobj = $this->prepare_quiz();
1067  
1068          $params = [
1069              'objectid' => 1,
1070              'context' => context_module::instance($quizobj->get_cmid()),
1071              'other' => [
1072                  'quizid' => $quizobj->get_quizid(),
1073                  'firstslotid' => 1,
1074                  'firstslotnumber' => 2,
1075                  'title' => 'New title'
1076              ]
1077          ];
1078          $event = \mod_quiz\event\section_break_created::create($params);
1079  
1080          // Trigger and capture the event.
1081          $sink = $this->redirectEvents();
1082          $event->trigger();
1083          $events = $sink->get_events();
1084          $event = reset($events);
1085  
1086          // Check that the event data is valid.
1087          $this->assertInstanceOf('\mod_quiz\event\section_break_created', $event);
1088          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1089          $this->assertStringContainsString($params['other']['title'], $event->get_description());
1090          $this->assertEventContextNotUsed($event);
1091      }
1092  
1093      /**
1094       * Test the section break deleted event.
1095       *
1096       * There is no external API for deleting section break, so the unit test will simply
1097       * create and trigger the event and ensure the event data is returned as expected.
1098       */
1099      public function test_section_break_deleted() {
1100          $quizobj = $this->prepare_quiz();
1101  
1102          $params = [
1103              'objectid' => 1,
1104              'context' => context_module::instance($quizobj->get_cmid()),
1105              'other' => [
1106                  'quizid' => $quizobj->get_quizid(),
1107                  'firstslotid' => 1,
1108                  'firstslotnumber' => 2
1109              ]
1110          ];
1111          $event = \mod_quiz\event\section_break_deleted::create($params);
1112  
1113          // Trigger and capture the event.
1114          $sink = $this->redirectEvents();
1115          $event->trigger();
1116          $events = $sink->get_events();
1117          $event = reset($events);
1118  
1119          // Check that the event data is valid.
1120          $this->assertInstanceOf('\mod_quiz\event\section_break_deleted', $event);
1121          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1122          $this->assertEventContextNotUsed($event);
1123      }
1124  
1125      /**
1126       * Test the section shuffle updated event.
1127       *
1128       * There is no external API for updating section shuffle, so the unit test will simply
1129       * create and trigger the event and ensure the event data is returned as expected.
1130       */
1131      public function test_section_shuffle_updated() {
1132          $quizobj = $this->prepare_quiz();
1133  
1134          $params = [
1135              'objectid' => 1,
1136              'context' => context_module::instance($quizobj->get_cmid()),
1137              'other' => [
1138                  'quizid' => $quizobj->get_quizid(),
1139                  'firstslotnumber' => 2,
1140                  'shuffle' => true
1141              ]
1142          ];
1143          $event = \mod_quiz\event\section_shuffle_updated::create($params);
1144  
1145          // Trigger and capture the event.
1146          $sink = $this->redirectEvents();
1147          $event->trigger();
1148          $events = $sink->get_events();
1149          $event = reset($events);
1150  
1151          // Check that the event data is valid.
1152          $this->assertInstanceOf('\mod_quiz\event\section_shuffle_updated', $event);
1153          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1154          $this->assertEventContextNotUsed($event);
1155      }
1156  
1157      /**
1158       * Test the section title updated event.
1159       *
1160       * There is no external API for updating section title, so the unit test will simply
1161       * create and trigger the event and ensure the event data is returned as expected.
1162       */
1163      public function test_section_title_updated() {
1164          $quizobj = $this->prepare_quiz();
1165  
1166          $params = [
1167              'objectid' => 1,
1168              'context' => context_module::instance($quizobj->get_cmid()),
1169              'other' => [
1170                  'quizid' => $quizobj->get_quizid(),
1171                  'firstslotid' => 1,
1172                  'firstslotnumber' => 2,
1173                  'newtitle' => 'New title'
1174              ]
1175          ];
1176          $event = \mod_quiz\event\section_title_updated::create($params);
1177  
1178          // Trigger and capture the event.
1179          $sink = $this->redirectEvents();
1180          $event->trigger();
1181          $events = $sink->get_events();
1182          $event = reset($events);
1183  
1184          // Check that the event data is valid.
1185          $this->assertInstanceOf('\mod_quiz\event\section_title_updated', $event);
1186          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1187          $this->assertStringContainsString($params['other']['newtitle'], $event->get_description());
1188          $this->assertEventContextNotUsed($event);
1189      }
1190  
1191      /**
1192       * Test the slot created event.
1193       *
1194       * There is no external API for creating slot, so the unit test will simply
1195       * create and trigger the event and ensure the event data is returned as expected.
1196       */
1197      public function test_slot_created() {
1198          $quizobj = $this->prepare_quiz();
1199  
1200          $params = [
1201              'objectid' => 1,
1202              'context' => context_module::instance($quizobj->get_cmid()),
1203              'other' => [
1204                  'quizid' => $quizobj->get_quizid(),
1205                  'slotnumber' => 1,
1206                  'page' => 1
1207              ]
1208          ];
1209          $event = \mod_quiz\event\slot_created::create($params);
1210  
1211          // Trigger and capture the event.
1212          $sink = $this->redirectEvents();
1213          $event->trigger();
1214          $events = $sink->get_events();
1215          $event = reset($events);
1216  
1217          // Check that the event data is valid.
1218          $this->assertInstanceOf('\mod_quiz\event\slot_created', $event);
1219          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1220          $this->assertEventContextNotUsed($event);
1221      }
1222  
1223      /**
1224       * Test the slot deleted event.
1225       *
1226       * There is no external API for deleting slot, so the unit test will simply
1227       * create and trigger the event and ensure the event data is returned as expected.
1228       */
1229      public function test_slot_deleted() {
1230          $quizobj = $this->prepare_quiz();
1231  
1232          $params = [
1233              'objectid' => 1,
1234              'context' => context_module::instance($quizobj->get_cmid()),
1235              'other' => [
1236                  'quizid' => $quizobj->get_quizid(),
1237                  'slotnumber' => 1,
1238              ]
1239          ];
1240          $event = \mod_quiz\event\slot_deleted::create($params);
1241  
1242          // Trigger and capture the event.
1243          $sink = $this->redirectEvents();
1244          $event->trigger();
1245          $events = $sink->get_events();
1246          $event = reset($events);
1247  
1248          // Check that the event data is valid.
1249          $this->assertInstanceOf('\mod_quiz\event\slot_deleted', $event);
1250          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1251          $this->assertEventContextNotUsed($event);
1252      }
1253  
1254      /**
1255       * Test the slot mark updated event.
1256       *
1257       * There is no external API for updating slot mark, so the unit test will simply
1258       * create and trigger the event and ensure the event data is returned as expected.
1259       */
1260      public function test_slot_mark_updated() {
1261          $quizobj = $this->prepare_quiz();
1262  
1263          $params = [
1264              'objectid' => 1,
1265              'context' => context_module::instance($quizobj->get_cmid()),
1266              'other' => [
1267                  'quizid' => $quizobj->get_quizid(),
1268                  'previousmaxmark' => 1,
1269                  'newmaxmark' => 2,
1270              ]
1271          ];
1272          $event = \mod_quiz\event\slot_mark_updated::create($params);
1273  
1274          // Trigger and capture the event.
1275          $sink = $this->redirectEvents();
1276          $event->trigger();
1277          $events = $sink->get_events();
1278          $event = reset($events);
1279  
1280          // Check that the event data is valid.
1281          $this->assertInstanceOf('\mod_quiz\event\slot_mark_updated', $event);
1282          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1283          $this->assertEventContextNotUsed($event);
1284      }
1285  
1286      /**
1287       * Test the slot moved event.
1288       *
1289       * There is no external API for moving slot, so the unit test will simply
1290       * create and trigger the event and ensure the event data is returned as expected.
1291       */
1292      public function test_slot_moved() {
1293          $quizobj = $this->prepare_quiz();
1294  
1295          $params = [
1296              'objectid' => 1,
1297              'context' => context_module::instance($quizobj->get_cmid()),
1298              'other' => [
1299                  'quizid' => $quizobj->get_quizid(),
1300                  'previousslotnumber' => 1,
1301                  'afterslotnumber' => 2,
1302                  'page' => 1
1303              ]
1304          ];
1305          $event = \mod_quiz\event\slot_moved::create($params);
1306  
1307          // Trigger and capture the event.
1308          $sink = $this->redirectEvents();
1309          $event->trigger();
1310          $events = $sink->get_events();
1311          $event = reset($events);
1312  
1313          // Check that the event data is valid.
1314          $this->assertInstanceOf('\mod_quiz\event\slot_moved', $event);
1315          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1316          $this->assertEventContextNotUsed($event);
1317      }
1318  
1319      /**
1320       * Test the slot require previous updated event.
1321       *
1322       * There is no external API for updating slot require previous option, so the unit test will simply
1323       * create and trigger the event and ensure the event data is returned as expected.
1324       */
1325      public function test_slot_requireprevious_updated() {
1326          $quizobj = $this->prepare_quiz();
1327  
1328          $params = [
1329              'objectid' => 1,
1330              'context' => context_module::instance($quizobj->get_cmid()),
1331              'other' => [
1332                  'quizid' => $quizobj->get_quizid(),
1333                  'requireprevious' => true
1334              ]
1335          ];
1336          $event = \mod_quiz\event\slot_requireprevious_updated::create($params);
1337  
1338          // Trigger and capture the event.
1339          $sink = $this->redirectEvents();
1340          $event->trigger();
1341          $events = $sink->get_events();
1342          $event = reset($events);
1343  
1344          // Check that the event data is valid.
1345          $this->assertInstanceOf('\mod_quiz\event\slot_requireprevious_updated', $event);
1346          $this->assertEquals(context_module::instance($quizobj->get_cmid()), $event->get_context());
1347          $this->assertEventContextNotUsed($event);
1348      }
1349  }