Search moodle.org's
Developer Documentation

See Release Notes

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

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * 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)
 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', $this->eventscorm->id, $this->eventcm->id);
 290          $this->assertEventLegacyLogData($expected, $event);
 291          $this->assertEventContextNotUsed($event);
 292      }
 293  
 294      /**
 295       * Tests for tracks viewed event validations.
 296       */
 297      public function test_tracks_viewed_event_validations() {
 298          $this->resetAfterTest();
 299          try {
 300              \mod_scorm\event\tracks_viewed::create(array(
 301                  'context' => \context_module::instance($this->eventcm->id),
 302                  'courseid' => $this->eventcourse->id,
 303                  'other' => array('attemptid' => 2, 'scoid' => 2)
 304              ));
 305              $this->fail("Event validation should not allow \\mod_scorm\\event\\tracks_viewed to be triggered without
 306                      other['instanceid']");
 307          } catch (\Exception $e) {
 308              $this->assertInstanceOf('coding_exception', $e);
 309          }
 310          try {
 311              \mod_scorm\event\tracks_viewed::create(array(
 312                  'context' => \context_module::instance($this->eventcm->id),
 313                  'courseid' => $this->eventcourse->id,
 314                  'other' => array('instanceid' => 2, 'scoid' => 2)
 315              ));
 316              $this->fail("Event validation should not allow \\mod_scorm\\event\\tracks_viewed to be triggered without
 317                      other['attemptid']");
 318          } catch (\Exception $e) {
 319              $this->assertInstanceOf('coding_exception', $e);
 320          }
 321  
 322          try {
 323              \mod_scorm\event\tracks_viewed::create(array(
 324                  'context' => \context_module::instance($this->eventcm->id),
 325                  'courseid' => $this->eventcourse->id,
 326                  'other' => array('attemptid' => 2, 'instanceid' => 2)
 327              ));
 328              $this->fail("Event validation should not allow \\mod_scorm\\event\\tracks_viewed to be triggered without
 329                      other['scoid']");
 330          } catch (\Exception $e) {
 331              $this->assertInstanceOf('coding_exception', $e);
 332          }
 333      }
 334  
 335      /**
 336       * Tests for userreport viewed event.
 337       *
 338       * There is no api involved so the best we can do is test validations and legacy log by triggering event manually.
 339       */
 340      public function test_user_report_viewed_event() {
 341          $this->resetAfterTest();
 342          $event = \mod_scorm\event\user_report_viewed::create(array(
 343              'relateduserid' => 5,
 344              'context' => \context_module::instance($this->eventcm->id),
 345              'courseid' => $this->eventcourse->id,
 346              'other' => array('attemptid' => 2, 'instanceid' => $this->eventscorm->id)
 347          ));
 348  
 349          // Trigger and capture the event.
 350          $sink = $this->redirectEvents();
 351          $event->trigger();
 352          $events = $sink->get_events();
 353          $event = reset($events);
 354  
 355          // Check that the legacy log data is valid.
 356          $expected = array($this->eventcourse->id, 'scorm', 'userreport', 'report/userreport.php?id=' .
 357                  $this->eventcm->id . '&user=5&attempt=' . 2, $this->eventscorm->id, $this->eventcm->id);
 358          $this->assertEventLegacyLogData($expected, $event);
 359          $this->assertEventContextNotUsed($event);
 360      }
 361  
 362      /**
 363       * Tests for userreport viewed event validations.
 364       */
 365      public function test_user_report_viewed_event_validations() {
 366          $this->resetAfterTest();
 367          try {
 368              \mod_scorm\event\user_report_viewed::create(array(
 369                  'context' => \context_module::instance($this->eventcm->id),
 370                  'courseid' => $this->eventcourse->id,
 371                  'other' => array('attemptid' => 2)
 372              ));
 373              $this->fail("Event validation should not allow \\mod_scorm\\event\\user_report_viewed to be triggered without
 374                      other['instanceid']");
 375          } catch (\Exception $e) {
 376              $this->assertInstanceOf('coding_exception', $e);
 377          }
 378          try {
 379              \mod_scorm\event\user_report_viewed::create(array(
 380                  'context' => \context_module::instance($this->eventcm->id),
 381                  'courseid' => $this->eventcourse->id,
 382                  'other' => array('instanceid' => 2)
 383              ));
 384              $this->fail("Event validation should not allow \\mod_scorm\\event\\user_report_viewed to be triggered without
 385                      other['attemptid']");
 386          } catch (\Exception $e) {
 387              $this->assertInstanceOf('coding_exception', $e);
 388          }
 389      }
 390  
 391      /**
 392       * dataProvider for test_scoreraw_submitted_event().
 393       */
 394      public function get_scoreraw_submitted_event_provider() {
 395          return array(
 396              // SCORM 1.2.
 397              // - cmi.core.score.raw.
 398              'cmi.core.score.raw => 100' => array('cmi.core.score.raw', '100'),
 399              'cmi.core.score.raw => 90' => array('cmi.core.score.raw', '90'),
 400              'cmi.core.score.raw => 50' => array('cmi.core.score.raw', '50'),
 401              'cmi.core.score.raw => 10' => array('cmi.core.score.raw', '10'),
 402              // Check an edge case (PHP empty() vs isset()): score value equals to '0'.
 403              'cmi.core.score.raw => 0' => array('cmi.core.score.raw', '0'),
 404              // SCORM 1.3 AKA 2004.
 405              // - cmi.score.raw.
 406              'cmi.score.raw => 100' => array('cmi.score.raw', '100'),
 407              'cmi.score.raw => 90' => array('cmi.score.raw', '90'),
 408              'cmi.score.raw => 50' => array('cmi.score.raw', '50'),
 409              'cmi.score.raw => 10' => array('cmi.score.raw', '10'),
 410              // Check an edge case (PHP empty() vs isset()): score value equals to '0'.
 411              'cmi.score.raw => 0' => array('cmi.score.raw', '0'),
 412          );
 413      }
 414  
 415      /**
 416       * Tests for score submitted event.
 417       *
 418       * There is no api involved so the best we can do is test data by triggering event manually.
 419       *
 420       * @dataProvider get_scoreraw_submitted_event_provider
 421       *
 422       * @param string $cmielement a valid CMI raw score element
 423       * @param string $cmivalue a valid CMI raw score value
 424       */
 425      public function test_scoreraw_submitted_event($cmielement, $cmivalue) {
 426          $this->resetAfterTest();
 427          $event = \mod_scorm\event\scoreraw_submitted::create(array(
 428              'other' => array('attemptid' => '2', 'cmielement' => $cmielement, 'cmivalue' => $cmivalue),
 429              'objectid' => $this->eventscorm->id,
 430              'context' => \context_module::instance($this->eventcm->id),
 431              'relateduserid' => $this->eventuser->id
 432          ));
 433  
 434          // Trigger and capture the event.
 435          $sink = $this->redirectEvents();
 436          $event->trigger();
 437          $events = $sink->get_events();
 438          $sink->close();
 439          $event = reset($events);
 440          $this->assertEquals(2, $event->other['attemptid']);
 441          $this->assertEquals($cmielement, $event->other['cmielement']);
 442          $this->assertEquals($cmivalue, $event->other['cmivalue']);
 443  
 444          // Check that no legacy log data is provided.
 445          $this->assertEventLegacyLogData(null, $event);
 446          $this->assertEventContextNotUsed($event);
 447      }
 448  
 449      /**
 450       * dataProvider for test_scoreraw_submitted_event_validations().
 451       */
 452      public function get_scoreraw_submitted_event_validations() {
 453          return array(
 454              'scoreraw_submitted => missing cmielement' => array(
 455                  null, '50',
 456                  "Event validation should not allow \\mod_scorm\\event\\scoreraw_submitted " .
 457                      "to be triggered without other['cmielement']",
 458                  'Coding error detected, it must be fixed by a programmer: ' .
 459                      "The 'cmielement' must be set in other."
 460              ),
 461              'scoreraw_submitted => missing cmivalue' => array(
 462                  'cmi.core.score.raw', null,
 463                  "Event validation should not allow \\mod_scorm\\event\\scoreraw_submitted " .
 464                      "to be triggered without other['cmivalue']",
 465                  'Coding error detected, it must be fixed by a programmer: ' .
 466                      "The 'cmivalue' must be set in other."
 467              ),
 468              'scoreraw_submitted => wrong CMI element' => array(
 469                  'cmi.core.lesson_status', '50',
 470                  "Event validation should not allow \\mod_scorm\\event\\scoreraw_submitted " .
 471                      'to be triggered with a CMI element not representing a raw score',
 472                  'Coding error detected, it must be fixed by a programmer: ' .
 473                      "The 'cmielement' must represents a valid CMI raw score (cmi.core.lesson_status)."
 474              ),
 475          );
 476      }
 477  
 478      /**
 479       * Tests for score submitted event validations.
 480       *
 481       * @dataProvider get_scoreraw_submitted_event_validations
 482       *
 483       * @param string $cmielement a valid CMI raw score element
 484       * @param string $cmivalue a valid CMI raw score value
 485       * @param string $failmessage the message used to fail the test in case of missing to violate a validation rule
 486       * @param string $excmessage the exception message when violating the validations rules
 487       */
 488      public function test_scoreraw_submitted_event_validations($cmielement, $cmivalue, $failmessage, $excmessage) {
 489          $this->resetAfterTest();
 490          try {
 491              $data = array(
 492                  'context' => \context_module::instance($this->eventcm->id),
 493                  'courseid' => $this->eventcourse->id,
 494                  'other' => array('attemptid' => 2)
 495              );
 496              if ($cmielement != null) {
 497                  $data['other']['cmielement'] = $cmielement;
 498              }
 499              if ($cmivalue != null) {
 500                  $data['other']['cmivalue'] = $cmivalue;
 501              }
 502              \mod_scorm\event\scoreraw_submitted::create($data);
 503              $this->fail($failmessage);
 504          } catch (\Exception $e) {
 505              $this->assertInstanceOf('coding_exception', $e);
 506              $this->assertEquals($excmessage, $e->getMessage());
 507          }
 508      }
 509  
 510      /**
 511       * dataProvider for test_status_submitted_event().
 512       */
 513      public function get_status_submitted_event_provider() {
 514          return array(
 515              // SCORM 1.2.
 516              // 1. Status: cmi.core.lesson_status.
 517              'cmi.core.lesson_status => passed' => array('cmi.core.lesson_status', 'passed'),
 518              'cmi.core.lesson_status => completed' => array('cmi.core.lesson_status', 'completed'),
 519              'cmi.core.lesson_status => failed' => array('cmi.core.lesson_status', 'failed'),
 520              'cmi.core.lesson_status => incomplete' => array('cmi.core.lesson_status', 'incomplete'),
 521              'cmi.core.lesson_status => browsed' => array('cmi.core.lesson_status', 'browsed'),
 522              'cmi.core.lesson_status => not attempted' => array('cmi.core.lesson_status', 'not attempted'),
 523              // SCORM 1.3 AKA 2004.
 524              // 1. Completion status: cmi.completion_status.
 525              'cmi.completion_status => completed' => array('cmi.completion_status', 'completed'),
 526              'cmi.completion_status => incomplete' => array('cmi.completion_status', 'incomplete'),
 527              'cmi.completion_status => not attempted' => array('cmi.completion_status', 'not attempted'),
 528              'cmi.completion_status => unknown' => array('cmi.completion_status', 'unknown'),
 529              // 2. Success status: cmi.success_status.
 530              'cmi.success_status => passed' => array('cmi.success_status', 'passed'),
 531              'cmi.success_status => failed' => array('cmi.success_status', 'failed'),
 532              'cmi.success_status => unknown' => array('cmi.success_status', 'unknown')
 533          );
 534      }
 535  
 536      /**
 537       * Tests for status submitted event.
 538       *
 539       * There is no api involved so the best we can do is test data by triggering event manually.
 540       *
 541       * @dataProvider get_status_submitted_event_provider
 542       *
 543       * @param string $cmielement a valid CMI status element
 544       * @param string $cmivalue a valid CMI status value
 545       */
 546      public function test_status_submitted_event($cmielement, $cmivalue) {
 547          $this->resetAfterTest();
 548          $event = \mod_scorm\event\status_submitted::create(array(
 549              'other' => array('attemptid' => '2', 'cmielement' => $cmielement, 'cmivalue' => $cmivalue),
 550              'objectid' => $this->eventscorm->id,
 551              'context' => \context_module::instance($this->eventcm->id),
 552              'relateduserid' => $this->eventuser->id
 553          ));
 554  
 555          // Trigger and capture the event.
 556          $sink = $this->redirectEvents();
 557          $event->trigger();
 558          $events = $sink->get_events();
 559          $sink->close();
 560          $event = reset($events);
 561          $this->assertEquals(2, $event->other['attemptid']);
 562          $this->assertEquals($cmielement, $event->other['cmielement']);
 563          $this->assertEquals($cmivalue, $event->other['cmivalue']);
 564  
 565          // Check that no legacy log data is provided.
 566          $this->assertEventLegacyLogData(null, $event);
 567          $this->assertEventContextNotUsed($event);
 568      }
 569  
 570      /**
 571       * dataProvider for test_status_submitted_event_validations().
 572       */
 573      public function get_status_submitted_event_validations() {
 574          return array(
 575              'status_submitted => missing cmielement' => array(
 576                  null, 'passed',
 577                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 578                      "to be triggered without other['cmielement']",
 579                  'Coding error detected, it must be fixed by a programmer: ' .
 580                      "The 'cmielement' must be set in other."
 581              ),
 582              'status_submitted => missing cmivalue' => array(
 583                  'cmi.core.lesson_status', null,
 584                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 585                      "to be triggered without other['cmivalue']",
 586                  'Coding error detected, it must be fixed by a programmer: ' .
 587                      "The 'cmivalue' must be set in other."
 588              ),
 589              'status_submitted => wrong CMI element' => array(
 590                  'cmi.core.score.raw', 'passed',
 591                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 592                      'to be triggered with a CMI element not representing a valid CMI status element',
 593                  'Coding error detected, it must be fixed by a programmer: ' .
 594                      "The 'cmielement' must represents a valid CMI status element (cmi.core.score.raw)."
 595              ),
 596              'status_submitted => wrong CMI value' => array(
 597                  'cmi.core.lesson_status', 'blahblahblah',
 598                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 599                      'to be triggered with a CMI element not representing a valid CMI status',
 600                  'Coding error detected, it must be fixed by a programmer: ' .
 601                      "The 'cmivalue' must represents a valid CMI status value (blahblahblah)."
 602              ),
 603          );
 604      }
 605  
 606      /**
 607       * Tests for status submitted event validations.
 608       *
 609       * @dataProvider get_status_submitted_event_validations
 610       *
 611       * @param string $cmielement a valid CMI status element
 612       * @param string $cmivalue a valid CMI status value
 613       * @param string $failmessage the message used to fail the test in case of missing to violate a validation rule
 614       * @param string $excmessage the exception message when violating the validations rules
 615       */
 616      public function test_status_submitted_event_validations($cmielement, $cmivalue, $failmessage, $excmessage) {
 617          $this->resetAfterTest();
 618          try {
 619              $data = array(
 620                  'context' => \context_module::instance($this->eventcm->id),
 621                  'courseid' => $this->eventcourse->id,
 622                  'other' => array('attemptid' => 2)
 623              );
 624              if ($cmielement != null) {
 625                  $data['other']['cmielement'] = $cmielement;
 626              }
 627              if ($cmivalue != null) {
 628                  $data['other']['cmivalue'] = $cmivalue;
 629              }
 630              \mod_scorm\event\status_submitted::create($data);
 631              $this->fail($failmessage);
 632          } catch (\Exception $e) {
 633              $this->assertInstanceOf('coding_exception', $e);
 634              $this->assertEquals($excmessage, $e->getMessage());
 635          }
 636      }
 637  }