Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 311 and 403] [Versions 400 and 403] [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          $this->assertEventContextNotUsed($event);
  87  
  88          // Test event validations.
  89          $this->expectException(\coding_exception::class);
  90          \mod_scorm\event\attempt_deleted::create(array(
  91              'contextid' => 5,
  92              'relateduserid' => 2
  93          ));
  94      }
  95  
  96      /**
  97       * Tests for interactions viewed validations.
  98       */
  99      public function test_interactions_viewed_event_validations() {
 100          $this->resetAfterTest();
 101          try {
 102              \mod_scorm\event\interactions_viewed::create(array(
 103                  'context' => \context_module::instance($this->eventcm->id),
 104                  'courseid' => $this->eventcourse->id,
 105                  'other' => array('attemptid' => 2)
 106              ));
 107              $this->fail("Event validation should not allow \\mod_scorm\\event\\interactions_viewed to be triggered without
 108                      other['instanceid']");
 109          } catch (\Exception $e) {
 110              $this->assertInstanceOf('coding_exception', $e);
 111          }
 112          try {
 113              \mod_scorm\event\interactions_viewed::create(array(
 114                  'context' => \context_module::instance($this->eventcm->id),
 115                  'courseid' => $this->eventcourse->id,
 116                  'other' => array('instanceid' => 2)
 117              ));
 118              $this->fail("Event validation should not allow \\mod_scorm\\event\\interactions_viewed to be triggered without
 119                      other['attemptid']");
 120          } catch (\Exception $e) {
 121              $this->assertInstanceOf('coding_exception', $e);
 122          }
 123      }
 124  
 125      /**
 126       * Tests for tracks viewed event validations.
 127       */
 128      public function test_tracks_viewed_event_validations() {
 129          $this->resetAfterTest();
 130          try {
 131              \mod_scorm\event\tracks_viewed::create(array(
 132                  'context' => \context_module::instance($this->eventcm->id),
 133                  'courseid' => $this->eventcourse->id,
 134                  'other' => array('attemptid' => 2, 'scoid' => 2)
 135              ));
 136              $this->fail("Event validation should not allow \\mod_scorm\\event\\tracks_viewed to be triggered without
 137                      other['instanceid']");
 138          } catch (\Exception $e) {
 139              $this->assertInstanceOf('coding_exception', $e);
 140          }
 141          try {
 142              \mod_scorm\event\tracks_viewed::create(array(
 143                  'context' => \context_module::instance($this->eventcm->id),
 144                  'courseid' => $this->eventcourse->id,
 145                  'other' => array('instanceid' => 2, 'scoid' => 2)
 146              ));
 147              $this->fail("Event validation should not allow \\mod_scorm\\event\\tracks_viewed to be triggered without
 148                      other['attemptid']");
 149          } catch (\Exception $e) {
 150              $this->assertInstanceOf('coding_exception', $e);
 151          }
 152  
 153          try {
 154              \mod_scorm\event\tracks_viewed::create(array(
 155                  'context' => \context_module::instance($this->eventcm->id),
 156                  'courseid' => $this->eventcourse->id,
 157                  'other' => array('attemptid' => 2, 'instanceid' => 2)
 158              ));
 159              $this->fail("Event validation should not allow \\mod_scorm\\event\\tracks_viewed to be triggered without
 160                      other['scoid']");
 161          } catch (\Exception $e) {
 162              $this->assertInstanceOf('coding_exception', $e);
 163          }
 164      }
 165  
 166      /**
 167       * Tests for userreport viewed event validations.
 168       */
 169      public function test_user_report_viewed_event_validations() {
 170          $this->resetAfterTest();
 171          try {
 172              \mod_scorm\event\user_report_viewed::create(array(
 173                  'context' => \context_module::instance($this->eventcm->id),
 174                  'courseid' => $this->eventcourse->id,
 175                  'other' => array('attemptid' => 2)
 176              ));
 177              $this->fail("Event validation should not allow \\mod_scorm\\event\\user_report_viewed to be triggered without
 178                      other['instanceid']");
 179          } catch (\Exception $e) {
 180              $this->assertInstanceOf('coding_exception', $e);
 181          }
 182          try {
 183              \mod_scorm\event\user_report_viewed::create(array(
 184                  'context' => \context_module::instance($this->eventcm->id),
 185                  'courseid' => $this->eventcourse->id,
 186                  'other' => array('instanceid' => 2)
 187              ));
 188              $this->fail("Event validation should not allow \\mod_scorm\\event\\user_report_viewed to be triggered without
 189                      other['attemptid']");
 190          } catch (\Exception $e) {
 191              $this->assertInstanceOf('coding_exception', $e);
 192          }
 193      }
 194  
 195      /**
 196       * dataProvider for test_scoreraw_submitted_event().
 197       */
 198      public function get_scoreraw_submitted_event_provider() {
 199          return array(
 200              // SCORM 1.2.
 201              // - cmi.core.score.raw.
 202              'cmi.core.score.raw => 100' => array('cmi.core.score.raw', '100'),
 203              'cmi.core.score.raw => 90' => array('cmi.core.score.raw', '90'),
 204              'cmi.core.score.raw => 50' => array('cmi.core.score.raw', '50'),
 205              'cmi.core.score.raw => 10' => array('cmi.core.score.raw', '10'),
 206              // Check an edge case (PHP empty() vs isset()): score value equals to '0'.
 207              'cmi.core.score.raw => 0' => array('cmi.core.score.raw', '0'),
 208              // SCORM 1.3 AKA 2004.
 209              // - cmi.score.raw.
 210              'cmi.score.raw => 100' => array('cmi.score.raw', '100'),
 211              'cmi.score.raw => 90' => array('cmi.score.raw', '90'),
 212              'cmi.score.raw => 50' => array('cmi.score.raw', '50'),
 213              'cmi.score.raw => 10' => array('cmi.score.raw', '10'),
 214              // Check an edge case (PHP empty() vs isset()): score value equals to '0'.
 215              'cmi.score.raw => 0' => array('cmi.score.raw', '0'),
 216          );
 217      }
 218  
 219      /**
 220       * dataProvider for test_scoreraw_submitted_event_validations().
 221       */
 222      public function get_scoreraw_submitted_event_validations() {
 223          return array(
 224              'scoreraw_submitted => missing cmielement' => array(
 225                  null, '50',
 226                  "Event validation should not allow \\mod_scorm\\event\\scoreraw_submitted " .
 227                      "to be triggered without other['cmielement']",
 228                  'Coding error detected, it must be fixed by a programmer: ' .
 229                      "The 'cmielement' must be set in other."
 230              ),
 231              'scoreraw_submitted => missing cmivalue' => array(
 232                  'cmi.core.score.raw', null,
 233                  "Event validation should not allow \\mod_scorm\\event\\scoreraw_submitted " .
 234                      "to be triggered without other['cmivalue']",
 235                  'Coding error detected, it must be fixed by a programmer: ' .
 236                      "The 'cmivalue' must be set in other."
 237              ),
 238              'scoreraw_submitted => wrong CMI element' => array(
 239                  'cmi.core.lesson_status', '50',
 240                  "Event validation should not allow \\mod_scorm\\event\\scoreraw_submitted " .
 241                      'to be triggered with a CMI element not representing a raw score',
 242                  'Coding error detected, it must be fixed by a programmer: ' .
 243                      "The 'cmielement' must represents a valid CMI raw score (cmi.core.lesson_status)."
 244              ),
 245          );
 246      }
 247  
 248      /**
 249       * Tests for score submitted event validations.
 250       *
 251       * @dataProvider get_scoreraw_submitted_event_validations
 252       *
 253       * @param string $cmielement a valid CMI raw score element
 254       * @param string $cmivalue a valid CMI raw score value
 255       * @param string $failmessage the message used to fail the test in case of missing to violate a validation rule
 256       * @param string $excmessage the exception message when violating the validations rules
 257       */
 258      public function test_scoreraw_submitted_event_validations($cmielement, $cmivalue, $failmessage, $excmessage) {
 259          $this->resetAfterTest();
 260          try {
 261              $data = array(
 262                  'context' => \context_module::instance($this->eventcm->id),
 263                  'courseid' => $this->eventcourse->id,
 264                  'other' => array('attemptid' => 2)
 265              );
 266              if ($cmielement != null) {
 267                  $data['other']['cmielement'] = $cmielement;
 268              }
 269              if ($cmivalue != null) {
 270                  $data['other']['cmivalue'] = $cmivalue;
 271              }
 272              \mod_scorm\event\scoreraw_submitted::create($data);
 273              $this->fail($failmessage);
 274          } catch (\Exception $e) {
 275              $this->assertInstanceOf('coding_exception', $e);
 276              $this->assertEquals($excmessage, $e->getMessage());
 277          }
 278      }
 279  
 280      /**
 281       * dataProvider for test_status_submitted_event().
 282       */
 283      public function get_status_submitted_event_provider() {
 284          return array(
 285              // SCORM 1.2.
 286              // 1. Status: cmi.core.lesson_status.
 287              'cmi.core.lesson_status => passed' => array('cmi.core.lesson_status', 'passed'),
 288              'cmi.core.lesson_status => completed' => array('cmi.core.lesson_status', 'completed'),
 289              'cmi.core.lesson_status => failed' => array('cmi.core.lesson_status', 'failed'),
 290              'cmi.core.lesson_status => incomplete' => array('cmi.core.lesson_status', 'incomplete'),
 291              'cmi.core.lesson_status => browsed' => array('cmi.core.lesson_status', 'browsed'),
 292              'cmi.core.lesson_status => not attempted' => array('cmi.core.lesson_status', 'not attempted'),
 293              // SCORM 1.3 AKA 2004.
 294              // 1. Completion status: cmi.completion_status.
 295              'cmi.completion_status => completed' => array('cmi.completion_status', 'completed'),
 296              'cmi.completion_status => incomplete' => array('cmi.completion_status', 'incomplete'),
 297              'cmi.completion_status => not attempted' => array('cmi.completion_status', 'not attempted'),
 298              'cmi.completion_status => unknown' => array('cmi.completion_status', 'unknown'),
 299              // 2. Success status: cmi.success_status.
 300              'cmi.success_status => passed' => array('cmi.success_status', 'passed'),
 301              'cmi.success_status => failed' => array('cmi.success_status', 'failed'),
 302              'cmi.success_status => unknown' => array('cmi.success_status', 'unknown')
 303          );
 304      }
 305  
 306      /**
 307       * dataProvider for test_status_submitted_event_validations().
 308       */
 309      public function get_status_submitted_event_validations() {
 310          return array(
 311              'status_submitted => missing cmielement' => array(
 312                  null, 'passed',
 313                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 314                      "to be triggered without other['cmielement']",
 315                  'Coding error detected, it must be fixed by a programmer: ' .
 316                      "The 'cmielement' must be set in other."
 317              ),
 318              'status_submitted => missing cmivalue' => array(
 319                  'cmi.core.lesson_status', null,
 320                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 321                      "to be triggered without other['cmivalue']",
 322                  'Coding error detected, it must be fixed by a programmer: ' .
 323                      "The 'cmivalue' must be set in other."
 324              ),
 325              'status_submitted => wrong CMI element' => array(
 326                  'cmi.core.score.raw', 'passed',
 327                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 328                      'to be triggered with a CMI element not representing a valid CMI status element',
 329                  'Coding error detected, it must be fixed by a programmer: ' .
 330                      "The 'cmielement' must represents a valid CMI status element (cmi.core.score.raw)."
 331              ),
 332              'status_submitted => wrong CMI value' => array(
 333                  'cmi.core.lesson_status', 'blahblahblah',
 334                  "Event validation should not allow \\mod_scorm\\event\\status_submitted " .
 335                      'to be triggered with a CMI element not representing a valid CMI status',
 336                  'Coding error detected, it must be fixed by a programmer: ' .
 337                      "The 'cmivalue' must represents a valid CMI status value (blahblahblah)."
 338              ),
 339          );
 340      }
 341  
 342      /**
 343       * Tests for status submitted event validations.
 344       *
 345       * @dataProvider get_status_submitted_event_validations
 346       *
 347       * @param string $cmielement a valid CMI status element
 348       * @param string $cmivalue a valid CMI status value
 349       * @param string $failmessage the message used to fail the test in case of missing to violate a validation rule
 350       * @param string $excmessage the exception message when violating the validations rules
 351       */
 352      public function test_status_submitted_event_validations($cmielement, $cmivalue, $failmessage, $excmessage) {
 353          $this->resetAfterTest();
 354          try {
 355              $data = array(
 356                  'context' => \context_module::instance($this->eventcm->id),
 357                  'courseid' => $this->eventcourse->id,
 358                  'other' => array('attemptid' => 2)
 359              );
 360              if ($cmielement != null) {
 361                  $data['other']['cmielement'] = $cmielement;
 362              }
 363              if ($cmivalue != null) {
 364                  $data['other']['cmivalue'] = $cmivalue;
 365              }
 366              \mod_scorm\event\status_submitted::create($data);
 367              $this->fail($failmessage);
 368          } catch (\Exception $e) {
 369              $this->assertInstanceOf('coding_exception', $e);
 370              $this->assertEquals($excmessage, $e->getMessage());
 371          }
 372      }
 373  }