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   * This file contains tests for scorm events.
  19   *
  20   * @package    mod_scorm
  21   * @copyright  2013 onwards Ankit Agarwal
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace mod_scorm\event;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  require_once($CFG->dirroot . '/mod/scorm/locallib.php');
  31  require_once($CFG->dirroot . '/mod/scorm/lib.php');
  32  
  33  /**
  34   * Test class for various events related to Scorm.
  35   *
  36   * @package    mod_scorm
  37   * @copyright  2013 onwards Ankit Agarwal
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class events_test extends \advanced_testcase {
  41  
  42      /** @var stdClass store course object */
  43      protected $eventcourse;
  44  
  45      /** @var stdClass store user object */
  46      protected $eventuser;
  47  
  48      /** @var stdClass store scorm object */
  49      protected $eventscorm;
  50  
  51      /** @var stdClass store course module object */
  52      protected $eventcm;
  53  
  54      protected function setUp(): void {
  55          $this->setAdminUser();
  56          $this->eventcourse = $this->getDataGenerator()->create_course();
  57          $this->eventuser = $this->getDataGenerator()->create_user();
  58          $record = new \stdClass();
  59          $record->course = $this->eventcourse->id;
  60          $this->eventscorm = $this->getDataGenerator()->create_module('scorm', $record);
  61          $this->eventcm = get_coursemodule_from_instance('scorm', $this->eventscorm->id);
  62      }
  63  
  64      /**
  65       * Tests for attempt deleted event
  66       */
  67      public function test_attempt_deleted_event() {
  68  
  69          global $USER;
  70  
  71          $this->resetAfterTest();
  72          scorm_insert_track(2, $this->eventscorm->id, 1, 4, 'cmi.core.score.raw', 10);
  73          $sink = $this->redirectEvents();
  74          scorm_delete_attempt(2, $this->eventscorm, 4);
  75          $events = $sink->get_events();
  76          $sink->close();
  77          $event = reset($events);
  78  
  79          // Verify data.
  80          $this->assertCount(3, $events);
  81          $this->assertInstanceOf('\mod_scorm\event\attempt_deleted', $event);
  82          $this->assertEquals($USER->id, $event->userid);
  83          $this->assertEquals(\context_module::instance($this->eventcm->id), $event->get_context());
  84          $this->assertEquals(4, $event->other['attemptid']);
  85          $this->assertEquals(2, $event->relateduserid);
  86          $expected = array($this->eventcourse->id, 'scorm', 'delete attempts', 'report.php?id=' . $this->eventcm->id,
  87                  4, $this->eventcm->id);
  88          $this->assertEventLegacyLogData($expected, $events[0]);
  89          $this->assertEventContextNotUsed($event);
  90  
  91          // Test event validations.
  92          $this->expectException(\coding_exception::class);
  93          \mod_scorm\event\attempt_deleted::create(array(
  94              'contextid' => 5,
  95              'relateduserid' => 2
  96          ));
  97      }
  98  
  99      /**
 100       * Tests for course module viewed event.
 101       *
 102       * There is no api involved so the best we can do is test legacy data by triggering event manually.
 103       */
 104      public function test_course_module_viewed_event() {
 105          $this->resetAfterTest();
 106          $event = \mod_scorm\event\course_module_viewed::create(array(
 107              'objectid' => $this->eventscorm->id,
 108              'context' => \context_module::instance($this->eventcm->id),
 109              'courseid' => $this->eventcourse->id
 110          ));
 111  
 112          // Trigger and capture the event.
 113          $sink = $this->redirectEvents();
 114          $event->trigger();
 115          $events = $sink->get_events();
 116          $event = reset($events);
 117  
 118          // Check that the legacy log data is valid.
 119          $expected = array($this->eventcourse->id, 'scorm', 'pre-view', 'view.php?id=' . $this->eventcm->id,
 120                  $this->eventscorm->id, $this->eventcm->id);
 121          $this->assertEventLegacyLogData($expected, $event);
 122          $this->assertEventContextNotUsed($event);
 123      }
 124  
 125      /**
 126       * Tests for instance list viewed event.
 127       *
 128       * There is no api involved so the best we can do is test legacy data by triggering event manually.
 129       */
 130      public function test_course_module_instance_list_viewed_event() {
 131          $this->resetAfterTest();
 132          $event = \mod_scorm\event\course_module_instance_list_viewed::create(array(
 133              'context' => \context_course::instance($this->eventcourse->id),
 134              'courseid' => $this->eventcourse->id
 135          ));
 136  
 137          // Trigger and capture the event.
 138          $sink = $this->redirectEvents();
 139          $event->trigger();
 140          $events = $sink->get_events();
 141          $event = reset($events);
 142  
 143          // Check that the legacy log data is valid.
 144          $expected = array($this->eventcourse->id, 'scorm', 'view all', 'index.php?id=' . $this->eventcourse->id, '');
 145          $this->assertEventLegacyLogData($expected, $event);
 146          $this->assertEventContextNotUsed($event);
 147      }
 148  
 149      /**
 150       * Tests for interactions viewed.
 151       *
 152       * There is no api involved so the best we can do is test legacy data by triggering event manually and test validations.
 153       */
 154      public function test_interactions_viewed_event() {
 155          $this->resetAfterTest();
 156          $event = \mod_scorm\event\interactions_viewed::create(array(
 157              'relateduserid' => 5,
 158              'context' => \context_module::instance($this->eventcm->id),
 159              'courseid' => $this->eventcourse->id,
 160              'other' => array('attemptid' => 2, 'instanceid' => $this->eventscorm->id)
 161          ));
 162  
 163          // Trigger and capture the event.
 164          $sink = $this->redirectEvents();
 165          $event->trigger();
 166          $events = $sink->get_events();
 167          $event = reset($events);
 168  
 169          // Check that the legacy log data is valid.
 170          $expected = array($this->eventcourse->id, 'scorm', 'userreportinteractions', 'report/userreportinteractions.php?id=' .
 171                  $this->eventcm->id . '&user=5&attempt=' . 2, $this->eventscorm->id, $this->eventcm->id);
 172          $this->assertEventLegacyLogData($expected, $event);
 173          $this->assertEventContextNotUsed($event);
 174      }
 175  
 176      /**
 177       * Tests for interactions viewed validations.
 178       */
 179      public function test_interactions_viewed_event_validations() {
 180          $this->resetAfterTest();
 181          try {
 182              \mod_scorm\event\interactions_viewed::create(array(
 183                  'context' => \context_module::instance($this->eventcm->id),
 184                  'courseid' => $this->eventcourse->id,
 185                  'other' => array('attemptid' => 2)
 186              ));
 187              $this->fail("Event validation should not allow \\mod_scorm\\event\\interactions_viewed to be triggered without
 188                      other['instanceid']");
 189          } catch (\Exception $e) {
 190              $this->assertInstanceOf('coding_exception', $e);
 191          }
 192          try {
 193              \mod_scorm\event\interactions_viewed::create(array(
 194                  'context' => \context_module::instance($this->eventcm->id),
 195                  'courseid' => $this->eventcourse->id,
 196                  'other' => array('instanceid' => 2)
 197              ));
 198              $this->fail("Event validation should not allow \\mod_scorm\\event\\interactions_viewed to be triggered without
 199                      other['attemptid']");
 200          } catch (\Exception $e) {
 201              $this->assertInstanceOf('coding_exception', $e);
 202          }
 203      }
 204  
 205      /** Tests for report viewed.
 206       *
 207       * There is no api involved so the best we can do is test legacy data and validations by triggering event manually.
 208       */
 209      public function test_report_viewed_event() {
 210          $this->resetAfterTest();
 211          $event = \mod_scorm\event\report_viewed::create(array(
 212               'context' => \context_module::instance($this->eventcm->id),
 213               'courseid' => $this->eventcourse->id,
 214               'other' => array(
 215                   'scormid' => $this->eventscorm->id,
 216                   'mode' => 'basic'
 217               )
 218          ));
 219  
 220          // Trigger and capture the event.
 221          $sink = $this->redirectEvents();
 222          $event->trigger();
 223          $events = $sink->get_events();
 224          $event = reset($events);
 225  
 226          // Check that the legacy log data is valid.
 227          $expected = array($this->eventcourse->id, 'scorm', 'report', 'report.php?id=' . $this->eventcm->id . '&mode=basic',
 228                  $this->eventscorm->id, $this->eventcm->id);
 229          $this->assertEventLegacyLogData($expected, $event);
 230          $this->assertEventContextNotUsed($event);
 231      }
 232  
 233      /** Tests for sco launched event.
 234       *
 235       * There is no api involved so the best we can do is test legacy data and validations by triggering event manually.
 236       */
 237      public function test_sco_launched_event() {
 238          $this->resetAfterTest();
 239          $event = \mod_scorm\event\sco_launched::create(array(
 240               'objectid' => 2,
 241               'context' => \context_module::instance($this->eventcm->id),
 242               'courseid' => $this->eventcourse->id,
 243               'other' => array('loadedcontent' => 'url_to_content_that_was_laoded.php')
 244          ));
 245  
 246          // Trigger and capture the event.
 247          $sink = $this->redirectEvents();
 248          $event->trigger();
 249          $events = $sink->get_events();
 250          $event = reset($events);
 251  
 252          // Check that the legacy log data is valid.
 253          $expected = array($this->eventcourse->id, 'scorm', 'launch', 'view.php?id=' . $this->eventcm->id,
 254                            'url_to_content_that_was_laoded.php', $this->eventcm->id);
 255          $this->assertEventLegacyLogData($expected, $event);
 256          $this->assertEventContextNotUsed($event);
 257  
 258          // Test validations.
 259          $this->expectException(\coding_exception::class);
 260          \mod_scorm\event\sco_launched::create(array(
 261               'objectid' => $this->eventscorm->id,
 262               'context' => \context_module::instance($this->eventcm->id),
 263               'courseid' => $this->eventcourse->id,
 264          ));
 265      }
 266  
 267      /**
 268       * Tests for tracks viewed event.
 269       *
 270       * There is no api involved so the best we can do is test validations by triggering event manually.
 271       */
 272      public function test_tracks_viewed_event() {
 273          $this->resetAfterTest();
 274          $event = \mod_scorm\event\tracks_viewed::create(array(
 275              'relateduserid' => 5,
 276              'context' => \context_module::instance($this->eventcm->id),
 277              'courseid' => $this->eventcourse->id,
 278              'other' => array('attemptid' => 2, 'instanceid' => $this->eventscorm->id, 'scoid' => 3, 'mode' => 'interactions')
 279          ));
 280  
 281          // Trigger and capture the event.
 282          $sink = $this->redirectEvents();
 283          $event->trigger();
 284          $events = $sink->get_events();
 285          $event = reset($events);
 286  
 287          // Check that the legacy log data is valid.
 288          $expected = array($this->eventcourse->id, 'scorm', 'userreporttracks', 'report/userreporttracks.php?id=' .
 289                  $this->eventcm->id . '&user=5&attempt=' . 2 . '&scoid=3' . '&mode=interactions',
 290                  $this->eventscorm->id, $this->eventcm->id);
 291          $this->assertEventLegacyLogData($expected, $event);
 292          $this->assertEventContextNotUsed($event);
 293      }
 294  
 295      /**
 296       * Tests for tracks viewed event validations.
 297       */
 298      public function test_tracks_viewed_event_validations() {
 299          $this->resetAfterTest();
 300          try {
 301              \mod_scorm\event\tracks_viewed::create(array(
 302                  'context' => \context_module::instance($this->eventcm->id),
 303                  'courseid' => $this->eventcourse->id,
 304                  'other' => array('attemptid' => 2, 'scoid' => 2)
 305              ));
 306              $this->fail("Event validation should not allow \\mod_scorm\\event\\tracks_viewed to be triggered without
 307                      other['instanceid']");
 308          } catch (\Exception $e) {
 309              $this->assertInstanceOf('coding_exception', $e);
 310          }
 311          try {
 312              \mod_scorm\event\tracks_viewed::create(array(
 313                  'context' => \context_module::instance($this->eventcm->id),
 314                  'courseid' => $this->eventcourse->id,
 315                  'other' => array('instanceid' => 2, 'scoid' => 2)
 316              ));
 317              $this->fail("Event validation should not allow \\mod_scorm\\event\\tracks_viewed to be triggered without
 318                      other['attemptid']");
 319          } catch (\Exception $e) {
 320              $this->assertInstanceOf('coding_exception', $e);
 321          }
 322  
 323          try {
 324              \mod_scorm\event\tracks_viewed::create(array(
 325                  'context' => \context_module::instance($this->eventcm->id),
 326                  'courseid' => $this->eventcourse->id,
 327                  'other' => array('attemptid' => 2, 'instanceid' => 2)
 328              ));
 329              $this->fail("Event validation should not allow \\mod_scorm\\event\\tracks_viewed to be triggered without
 330                      other['scoid']");
 331          } catch (\Exception $e) {
 332              $this->assertInstanceOf('coding_exception', $e);
 333          }
 334      }
 335  
 336      /**
 337       * Tests for userreport viewed event.
 338       *
 339       * There is no api involved so the best we can do is test validations and legacy log by triggering event manually.
 340       */
 341      public function test_user_report_viewed_event() {
 342          $this->resetAfterTest();
 343          $event = \mod_scorm\event\user_report_viewed::create(array(
 344              'relateduserid' => 5,
 345              'context' => \context_module::instance($this->eventcm->id),
 346              'courseid' => $this->eventcourse->id,
 347              'other' => array('attemptid' => 2, 'instanceid' => $this->eventscorm->id)
 348          ));
 349  
 350          // Trigger and capture the event.
 351          $sink = $this->redirectEvents();
 352          $event->trigger();
 353          $events = $sink->get_events();
 354          $event = reset($events);
 355  
 356          // Check that the legacy log data is valid.
 357          $expected = array($this->eventcourse->id, 'scorm', 'userreport', 'report/userreport.php?id=' .
 358                  $this->eventcm->id . '&user=5&attempt=' . 2, $this->eventscorm->id, $this->eventcm->id);
 359          $this->assertEventLegacyLogData($expected, $event);
 360          $this->assertEventContextNotUsed($event);
 361      }
 362  
 363      /**
 364       * Tests for userreport viewed event validations.
 365       */
 366      public function test_user_report_viewed_event_validations() {
 367          $this->resetAfterTest();
 368          try {
 369              \mod_scorm\event\user_report_viewed::create(array(
 370                  'context' => \context_module::instance($this->eventcm->id),
 371                  'courseid' => $this->eventcourse->id,
 372                  'other' => array('attemptid' => 2)
 373              ));
 374              $this->fail("Event validation should not allow \\mod_scorm\\event\\user_report_viewed to be triggered without
 375                      other['instanceid']");
 376          } catch (\Exception $e) {
 377              $this->assertInstanceOf('coding_exception', $e);
 378          }
 379          try {
 380              \mod_scorm\event\user_report_viewed::create(array(
 381                  'context' => \context_module::instance($this->eventcm->id),
 382                  'courseid' => $this->eventcourse->id,
 383                  'other' => array('instanceid' => 2)
 384              ));
 385              $this->fail("Event validation should not allow \\mod_scorm\\event\\user_report_viewed to be triggered without
 386                      other['attemptid']");
 387          } catch (\Exception $e) {
 388              $this->assertInstanceOf('coding_exception', $e);
 389          }
 390      }
 391  
 392      /**
 393       * dataProvider for test_scoreraw_submitted_event().
 394       */
 395      public function get_scoreraw_submitted_event_provider() {
 396          return array(
 397              // SCORM 1.2.
 398              // - cmi.core.score.raw.
 399              'cmi.core.score.raw => 100' => array('cmi.core.score.raw', '100'),
 400              'cmi.core.score.raw => 90' => array('cmi.core.score.raw', '90'),
 401              'cmi.core.score.raw => 50' => array('cmi.core.score.raw', '50'),
 402              'cmi.core.score.raw => 10' => array('cmi.core.score.raw', '10'),
 403              // Check an edge case (PHP empty() vs isset()): score value equals to '0'.
 404              'cmi.core.score.raw => 0' => array('cmi.core.score.raw', '0'),
 405              // SCORM 1.3 AKA 2004.
 406              // - cmi.score.raw.
 407              'cmi.score.raw => 100' => array('cmi.score.raw', '100'),
 408              'cmi.score.raw => 90' => array('cmi.score.raw', '90'),
 409              'cmi.score.raw => 50' => array('cmi.score.raw', '50'),
 410              'cmi.score.raw => 10' => array('cmi.score.raw', '10'),
 411              // Check an edge case (PHP empty() vs isset()): score value equals to '0'.
 412              'cmi.score.raw => 0' => array('cmi.score.raw', '0'),
 413          );
 414      }
 415  
 416      /**
 417       * Tests for score submitted event.
 418       *
 419       * There is no api involved so the best we can do is test data by triggering event manually.
 420       *
 421       * @dataProvider get_scoreraw_submitted_event_provider
 422       *
 423       * @param string $cmielement a valid CMI raw score element
 424       * @param string $cmivalue a valid CMI raw score value
 425       */
 426      public function test_scoreraw_submitted_event($cmielement, $cmivalue) {
 427          $this->resetAfterTest();
 428          $event = \mod_scorm\event\scoreraw_submitted::create(array(
 429              'other' => array('attemptid' => '2', 'cmielement' => $cmielement, 'cmivalue' => $cmivalue),
 430              'objectid' => $this->eventscorm->id,
 431              'context' => \context_module::instance($this->eventcm->id),
 432              'relateduserid' => $this->eventuser->id
 433          ));
 434  
 435          // Trigger and capture the event.
 436          $sink = $this->redirectEvents();
 437          $event->trigger();
 438          $events = $sink->get_events();
 439          $sink->close();
 440          $event = reset($events);
 441          $this->assertEquals(2, $event->other['attemptid']);
 442          $this->assertEquals($cmielement, $event->other['cmielement']);
 443          $this->assertEquals($cmivalue, $event->other['cmivalue']);
 444  
 445          // Check that no legacy log data is provided.
 446          $this->assertEventLegacyLogData(null, $event);
 447          $this->assertEventContextNotUsed($event);
 448      }
 449  
 450      /**
 451       * dataProvider for test_scoreraw_submitted_event_validations().
 452       */
 453      public function get_scoreraw_submitted_event_validations() {
 454          return array(
 455              'scoreraw_submitted => missing cmielement' => array(
 456                  null, '50',
 457                  "Event validation should not allow \\mod_scorm\\event\\scoreraw_submitted " .
 458                      "to be triggered without other['cmielement']",
 459                  'Coding error detected, it must be fixed by a programmer: ' .
 460                      "The 'cmielement' must be set in other."
 461              ),
 462              'scoreraw_submitted => missing cmivalue' => array(
 463                  'cmi.core.score.raw', null,
 464                  "Event validation should not allow \\mod_scorm\\event\\scoreraw_submitted " .
 465                      "to be triggered without other['cmivalue']",
 466                  'Coding error detected, it must be fixed by a programmer: ' .
 467                      "The 'cmivalue' must be set in other."
 468              ),
 469              'scoreraw_submitted => wrong CMI element' => array(
 470                  'cmi.core.lesson_status', '50',
 471                  "Event validation should not allow \\mod_scorm\\event\\scoreraw_submitted " .
 472                      'to be triggered with a CMI element not representing a raw score',
 473                  'Coding error detected, it must be fixed by a programmer: ' .
 474                      "The 'cmielement' must represents a valid CMI raw score (cmi.core.lesson_status)."
 475              ),
 476          );
 477      }
 478  
 479      /**
 480       * Tests for score submitted event validations.
 481       *
 482       * @dataProvider get_scoreraw_submitted_event_validations
 483       *
 484       * @param string $cmielement a valid CMI raw score element
 485       * @param string $cmivalue a valid CMI raw score value
 486       * @param string $failmessage the message used to fail the test in case of missing to violate a validation rule
 487       * @param string $excmessage the exception message when violating the validations rules
 488       */
 489      public function test_scoreraw_submitted_event_validations($cmielement, $cmivalue, $failmessage, $excmessage) {
 490          $this->resetAfterTest();
 491          try {
 492              $data = array(
 493                  'context' => \context_module::instance($this->eventcm->id),
 494                  'courseid' => $this->eventcourse->id,
 495                  'other' => array('attemptid' => 2)
 496              );
 497              if ($cmielement != null) {
 498                  $data['other']['cmielement'] = $cmielement;
 499              }
 500              if ($cmivalue != null) {
 501                  $data['other']['cmivalue'] = $cmivalue;
 502              }
 503              \mod_scorm\event\scoreraw_submitted::create($data);
 504              $this->fail($failmessage);
 505          } catch (\Exception $e) {
 506              $this->assertInstanceOf('coding_exception', $e);
 507              $this->assertEquals($excmessage, $e->getMessage());
 508          }
 509      }
 510  
 511      /**
 512       * dataProvider for test_status_submitted_event().
 513       */
 514      public function get_status_submitted_event_provider() {
 515          return array(
 516              // SCORM 1.2.
 517              // 1. Status: cmi.core.lesson_status.
 518              'cmi.core.lesson_status => passed' => array('cmi.core.lesson_status', 'passed'),
 519              'cmi.core.lesson_status => completed' => array('cmi.core.lesson_status', 'completed'),
 520              'cmi.core.lesson_status => failed' => array('cmi.core.lesson_status', 'failed'),
 521              'cmi.core.lesson_status => incomplete' => array('cmi.core.lesson_status', 'incomplete'),
 522              'cmi.core.lesson_status => browsed' => array('cmi.core.lesson_status', 'browsed'),
 523              'cmi.core.lesson_status => not attempted' => array('cmi.core.lesson_status', 'not attempted'),
 524              // SCORM 1.3 AKA 2004.
 525              // 1. Completion status: cmi.completion_status.
 526              'cmi.completion_status => completed' => array('cmi.completion_status', 'completed'),
 527              'cmi.completion_status => incomplete' => array('cmi.completion_status', 'incomplete'),
 528              'cmi.completion_status => not attempted' => array('cmi.completion_status', 'not attempted'),
 529              'cmi.completion_status => unknown' => array('cmi.completion_status', 'unknown'),
 530              // 2. Success status: cmi.success_status.
 531              'cmi.success_status => passed' => array('cmi.success_status', 'passed'),
 532              'cmi.success_status => failed' => array('cmi.success_status', 'failed'),
 533              'cmi.success_status => unknown' => array('cmi.success_status', 'unknown')
 534          );
 535      }
 536  
 537      /**
 538       * Tests for status submitted event.
 539       *
 540       * There is no api involved so the best we can do is test data by triggering event manually.
 541       *
 542       * @dataProvider get_status_submitted_event_provider
 543       *
 544       * @param string $cmielement a valid CMI status element
 545       * @param string $cmivalue a valid CMI status value
 546       */
 547      public function test_status_submitted_event($cmielement, $cmivalue) {
 548          $this->resetAfterTest();
 549          $event = \mod_scorm\event\status_submitted::create(array(
 550              'other' => array('attemptid' => '2', 'cmielement' => $cmielement, 'cmivalue' => $cmivalue),
 551              'objectid' => $this->eventscorm->id,
 552              'context' => \context_module::instance($this->eventcm->id),
 553              'relateduserid' => $this->eventuser->id
 554          ));
 555  
 556          // Trigger and capture the event.
 557          $sink = $this->redirectEvents();
 558          $event->trigger();
 559          $events = $sink->get_events();
 560          $sink->close();
 561          $event = reset($events);
 562          $this->assertEquals(2, $event->other['attemptid']);
 563          $this->assertEquals($cmielement, $event->other['cmielement']);
 564          $this->assertEquals($cmivalue, $event->other['cmivalue']);
 565  
 566          // Check that no legacy log data is provided.
 567          $this->assertEventLegacyLogData(null, $event);
 568          $this->assertEventContextNotUsed($event);
 569      }
 570  
 571      /**
 572       * dataProvider for test_status_submitted_event_validations().
 573       */
 574      public function get_status_submitted_event_validations() {
 575          return array(
 576              'status_submitted => missing cmielement' => array(
 577                  null, 'passed',
 578                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 579                      "to be triggered without other['cmielement']",
 580                  'Coding error detected, it must be fixed by a programmer: ' .
 581                      "The 'cmielement' must be set in other."
 582              ),
 583              'status_submitted => missing cmivalue' => array(
 584                  'cmi.core.lesson_status', null,
 585                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 586                      "to be triggered without other['cmivalue']",
 587                  'Coding error detected, it must be fixed by a programmer: ' .
 588                      "The 'cmivalue' must be set in other."
 589              ),
 590              'status_submitted => wrong CMI element' => array(
 591                  'cmi.core.score.raw', 'passed',
 592                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 593                      'to be triggered with a CMI element not representing a valid CMI status element',
 594                  'Coding error detected, it must be fixed by a programmer: ' .
 595                      "The 'cmielement' must represents a valid CMI status element (cmi.core.score.raw)."
 596              ),
 597              'status_submitted => wrong CMI value' => array(
 598                  'cmi.core.lesson_status', 'blahblahblah',
 599                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 600                      'to be triggered with a CMI element not representing a valid CMI status',
 601                  'Coding error detected, it must be fixed by a programmer: ' .
 602                      "The 'cmivalue' must represents a valid CMI status value (blahblahblah)."
 603              ),
 604          );
 605      }
 606  
 607      /**
 608       * Tests for status submitted event validations.
 609       *
 610       * @dataProvider get_status_submitted_event_validations
 611       *
 612       * @param string $cmielement a valid CMI status element
 613       * @param string $cmivalue a valid CMI status value
 614       * @param string $failmessage the message used to fail the test in case of missing to violate a validation rule
 615       * @param string $excmessage the exception message when violating the validations rules
 616       */
 617      public function test_status_submitted_event_validations($cmielement, $cmivalue, $failmessage, $excmessage) {
 618          $this->resetAfterTest();
 619          try {
 620              $data = array(
 621                  'context' => \context_module::instance($this->eventcm->id),
 622                  'courseid' => $this->eventcourse->id,
 623                  'other' => array('attemptid' => 2)
 624              );
 625              if ($cmielement != null) {
 626                  $data['other']['cmielement'] = $cmielement;
 627              }
 628              if ($cmivalue != null) {
 629                  $data['other']['cmivalue'] = $cmivalue;
 630              }
 631              \mod_scorm\event\status_submitted::create($data);
 632              $this->fail($failmessage);
 633          } catch (\Exception $e) {
 634              $this->assertInstanceOf('coding_exception', $e);
 635              $this->assertEquals($excmessage, $e->getMessage());
 636          }
 637      }
 638  }