"Fossies" - the Fresh Open Source Software Archive

Member "moodle/mod/scorm/tests/lib_test.php" (6 Sep 2019, 34949 Bytes) of package /linux/www/moodle-3.6.6.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "lib_test.php": 3.6.5_vs_3.6.6.

    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  * SCORM module library functions tests
   19  *
   20  * @package    mod_scorm
   21  * @category   test
   22  * @copyright  2015 Juan Leyva <juan@moodle.com>
   23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
   24  * @since      Moodle 3.0
   25  */
   26 
   27 defined('MOODLE_INTERNAL') || die();
   28 
   29 global $CFG;
   30 
   31 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
   32 require_once($CFG->dirroot . '/mod/scorm/lib.php');
   33 
   34 /**
   35  * SCORM module library functions tests
   36  *
   37  * @package    mod_scorm
   38  * @category   test
   39  * @copyright  2015 Juan Leyva <juan@moodle.com>
   40  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
   41  * @since      Moodle 3.0
   42  */
   43 class mod_scorm_lib_testcase extends externallib_advanced_testcase {
   44 
   45     /**
   46      * Set up for every test
   47      */
   48     public function setUp() {
   49         global $DB;
   50         $this->resetAfterTest();
   51         $this->setAdminUser();
   52 
   53         // Setup test data.
   54         $this->course = $this->getDataGenerator()->create_course();
   55         $this->scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $this->course->id));
   56         $this->context = context_module::instance($this->scorm->cmid);
   57         $this->cm = get_coursemodule_from_instance('scorm', $this->scorm->id);
   58 
   59         // Create users.
   60         $this->student = self::getDataGenerator()->create_user();
   61         $this->teacher = self::getDataGenerator()->create_user();
   62 
   63         // Users enrolments.
   64         $this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
   65         $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
   66         $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
   67         $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual');
   68     }
   69 
   70     /** Test scorm_check_mode
   71      *
   72      * @return void
   73      */
   74     public function test_scorm_check_mode() {
   75         global $CFG;
   76 
   77         $newattempt = 'on';
   78         $attempt = 1;
   79         $mode = 'normal';
   80         scorm_check_mode($this->scorm, $newattempt, $attempt, $this->student->id, $mode);
   81         $this->assertEquals('off', $newattempt);
   82 
   83         $scoes = scorm_get_scoes($this->scorm->id);
   84         $sco = array_pop($scoes);
   85         scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed');
   86         $newattempt = 'on';
   87         scorm_check_mode($this->scorm, $newattempt, $attempt, $this->student->id, $mode);
   88         $this->assertEquals('on', $newattempt);
   89 
   90         // Now do the same with a SCORM 2004 package.
   91         $record = new stdClass();
   92         $record->course = $this->course->id;
   93         $record->packagefilepath = $CFG->dirroot.'/mod/scorm/tests/packages/RuntimeBasicCalls_SCORM20043rdEdition.zip';
   94         $scorm13 = $this->getDataGenerator()->create_module('scorm', $record);
   95         $newattempt = 'on';
   96         $attempt = 1;
   97         $mode = 'normal';
   98         scorm_check_mode($scorm13, $newattempt, $attempt, $this->student->id, $mode);
   99         $this->assertEquals('off', $newattempt);
  100 
  101         $scoes = scorm_get_scoes($scorm13->id);
  102         $sco = array_pop($scoes);
  103         scorm_insert_track($this->student->id, $scorm13->id, $sco->id, 1, 'cmi.completion_status', 'completed');
  104 
  105         $newattempt = 'on';
  106         $attempt = 1;
  107         $mode = 'normal';
  108         scorm_check_mode($scorm13, $newattempt, $attempt, $this->student->id, $mode);
  109         $this->assertEquals('on', $newattempt);
  110     }
  111 
  112     /**
  113      * Test scorm_view
  114      * @return void
  115      */
  116     public function test_scorm_view() {
  117         global $CFG;
  118 
  119         // Trigger and capture the event.
  120         $sink = $this->redirectEvents();
  121 
  122         scorm_view($this->scorm, $this->course, $this->cm, $this->context);
  123 
  124         $events = $sink->get_events();
  125         $this->assertCount(1, $events);
  126         $event = array_shift($events);
  127 
  128         // Checking that the event contains the expected values.
  129         $this->assertInstanceOf('\mod_scorm\event\course_module_viewed', $event);
  130         $this->assertEquals($this->context, $event->get_context());
  131         $url = new \moodle_url('/mod/scorm/view.php', array('id' => $this->cm->id));
  132         $this->assertEquals($url, $event->get_url());
  133         $this->assertEventContextNotUsed($event);
  134         $this->assertNotEmpty($event->get_name());
  135     }
  136 
  137     /**
  138      * Test scorm_get_availability_status and scorm_require_available
  139      * @return void
  140      */
  141     public function test_scorm_check_and_require_available() {
  142         global $DB;
  143 
  144         $this->setAdminUser();
  145 
  146         // User override case.
  147         $this->scorm->timeopen = time() + DAYSECS;
  148         $this->scorm->timeclose = time() - DAYSECS;
  149         list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context);
  150         $this->assertEquals(true, $status);
  151         $this->assertCount(0, $warnings);
  152 
  153         // Now check with a student.
  154         list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context, $this->student->id);
  155         $this->assertEquals(false, $status);
  156         $this->assertCount(2, $warnings);
  157         $this->assertArrayHasKey('notopenyet', $warnings);
  158         $this->assertArrayHasKey('expired', $warnings);
  159         $this->assertEquals(userdate($this->scorm->timeopen), $warnings['notopenyet']);
  160         $this->assertEquals(userdate($this->scorm->timeclose), $warnings['expired']);
  161 
  162         // Reset the scorm's times.
  163         $this->scorm->timeopen = $this->scorm->timeclose = 0;
  164 
  165         // Set to the student user.
  166         self::setUser($this->student);
  167 
  168         // Usual case.
  169         list($status, $warnings) = scorm_get_availability_status($this->scorm, false);
  170         $this->assertEquals(true, $status);
  171         $this->assertCount(0, $warnings);
  172 
  173         // SCORM not open.
  174         $this->scorm->timeopen = time() + DAYSECS;
  175         list($status, $warnings) = scorm_get_availability_status($this->scorm, false);
  176         $this->assertEquals(false, $status);
  177         $this->assertCount(1, $warnings);
  178 
  179         // SCORM closed.
  180         $this->scorm->timeopen = 0;
  181         $this->scorm->timeclose = time() - DAYSECS;
  182         list($status, $warnings) = scorm_get_availability_status($this->scorm, false);
  183         $this->assertEquals(false, $status);
  184         $this->assertCount(1, $warnings);
  185 
  186         // SCORM not open and closed.
  187         $this->scorm->timeopen = time() + DAYSECS;
  188         list($status, $warnings) = scorm_get_availability_status($this->scorm, false);
  189         $this->assertEquals(false, $status);
  190         $this->assertCount(2, $warnings);
  191 
  192         // Now additional checkings with different parameters values.
  193         list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context);
  194         $this->assertEquals(false, $status);
  195         $this->assertCount(2, $warnings);
  196 
  197         // SCORM not open.
  198         $this->scorm->timeopen = time() + DAYSECS;
  199         $this->scorm->timeclose = 0;
  200         list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context);
  201         $this->assertEquals(false, $status);
  202         $this->assertCount(1, $warnings);
  203 
  204         // SCORM closed.
  205         $this->scorm->timeopen = 0;
  206         $this->scorm->timeclose = time() - DAYSECS;
  207         list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context);
  208         $this->assertEquals(false, $status);
  209         $this->assertCount(1, $warnings);
  210 
  211         // SCORM not open and closed.
  212         $this->scorm->timeopen = time() + DAYSECS;
  213         list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context);
  214         $this->assertEquals(false, $status);
  215         $this->assertCount(2, $warnings);
  216 
  217         // As teacher now.
  218         self::setUser($this->teacher);
  219 
  220         // SCORM not open and closed.
  221         $this->scorm->timeopen = time() + DAYSECS;
  222         list($status, $warnings) = scorm_get_availability_status($this->scorm, false);
  223         $this->assertEquals(false, $status);
  224         $this->assertCount(2, $warnings);
  225 
  226         // Now, we use the special capability.
  227         // SCORM not open and closed.
  228         $this->scorm->timeopen = time() + DAYSECS;
  229         list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context);
  230         $this->assertEquals(true, $status);
  231         $this->assertCount(0, $warnings);
  232 
  233         // Check exceptions does not broke anything.
  234         scorm_require_available($this->scorm, true, $this->context);
  235         // Now, expect exceptions.
  236         $this->expectException('moodle_exception');
  237         $this->expectExceptionMessage(get_string("notopenyet", "scorm", userdate($this->scorm->timeopen)));
  238 
  239         // Now as student other condition.
  240         self::setUser($this->student);
  241         $this->scorm->timeopen = 0;
  242         $this->scorm->timeclose = time() - DAYSECS;
  243 
  244         $this->expectException('moodle_exception');
  245         $this->expectExceptionMessage(get_string("expired", "scorm", userdate($this->scorm->timeclose)));
  246         scorm_require_available($this->scorm, false);
  247     }
  248 
  249     /**
  250      * Test scorm_get_last_completed_attempt
  251      *
  252      * @return void
  253      */
  254     public function test_scorm_get_last_completed_attempt() {
  255         $this->assertEquals(1, scorm_get_last_completed_attempt($this->scorm->id, $this->student->id));
  256     }
  257 
  258     public function test_scorm_core_calendar_provide_event_action_open() {
  259         $this->resetAfterTest();
  260 
  261         $this->setAdminUser();
  262 
  263         // Create a course.
  264         $course = $this->getDataGenerator()->create_course();
  265 
  266         // Create a scorm activity.
  267         $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id,
  268             'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS));
  269 
  270         // Create a calendar event.
  271         $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN);
  272 
  273         // Only students see scorm events.
  274         $this->setUser($this->student);
  275 
  276         // Create an action factory.
  277         $factory = new \core_calendar\action_factory();
  278 
  279         // Decorate action event.
  280         $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory);
  281 
  282         // Confirm the event was decorated.
  283         $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
  284         $this->assertEquals(get_string('enter', 'scorm'), $actionevent->get_name());
  285         $this->assertInstanceOf('moodle_url', $actionevent->get_url());
  286         $this->assertEquals(1, $actionevent->get_item_count());
  287         $this->assertTrue($actionevent->is_actionable());
  288     }
  289 
  290     public function test_scorm_core_calendar_provide_event_action_closed() {
  291         $this->resetAfterTest();
  292 
  293         $this->setAdminUser();
  294 
  295         // Create a course.
  296         $course = $this->getDataGenerator()->create_course();
  297 
  298         // Create a scorm activity.
  299         $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id,
  300             'timeclose' => time() - DAYSECS));
  301 
  302         // Create a calendar event.
  303         $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN);
  304 
  305         // Create an action factory.
  306         $factory = new \core_calendar\action_factory();
  307 
  308         // Decorate action event.
  309         $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory);
  310 
  311         // No event on the dashboard if module is closed.
  312         $this->assertNull($actionevent);
  313     }
  314 
  315     public function test_scorm_core_calendar_provide_event_action_open_in_future() {
  316         $this->resetAfterTest();
  317 
  318         $this->setAdminUser();
  319 
  320         // Create a course.
  321         $course = $this->getDataGenerator()->create_course();
  322 
  323         // Create a scorm activity.
  324         $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id,
  325             'timeopen' => time() + DAYSECS));
  326 
  327         // Create a calendar event.
  328         $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN);
  329 
  330         // Only students see scorm events.
  331         $this->setUser($this->student);
  332 
  333         // Create an action factory.
  334         $factory = new \core_calendar\action_factory();
  335 
  336         // Decorate action event.
  337         $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory);
  338 
  339         // Confirm the event was decorated.
  340         $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
  341         $this->assertEquals(get_string('enter', 'scorm'), $actionevent->get_name());
  342         $this->assertInstanceOf('moodle_url', $actionevent->get_url());
  343         $this->assertEquals(1, $actionevent->get_item_count());
  344         $this->assertFalse($actionevent->is_actionable());
  345     }
  346 
  347     public function test_scorm_core_calendar_provide_event_action_with_different_user_as_admin() {
  348         $this->resetAfterTest();
  349 
  350         $this->setAdminUser();
  351 
  352         // Create a course.
  353         $course = $this->getDataGenerator()->create_course();
  354 
  355         // Create a scorm activity.
  356         $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id,
  357             'timeopen' => time() + DAYSECS));
  358 
  359         // Create a calendar event.
  360         $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN);
  361 
  362         // Create an action factory.
  363         $factory = new \core_calendar\action_factory();
  364 
  365         // Decorate action event override with a passed in user.
  366         $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory, $this->student->id);
  367         $actionevent2 = mod_scorm_core_calendar_provide_event_action($event, $factory);
  368 
  369         // Only students see scorm events.
  370         $this->assertNull($actionevent2);
  371 
  372         // Confirm the event was decorated.
  373         $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
  374         $this->assertEquals(get_string('enter', 'scorm'), $actionevent->get_name());
  375         $this->assertInstanceOf('moodle_url', $actionevent->get_url());
  376         $this->assertEquals(1, $actionevent->get_item_count());
  377         $this->assertFalse($actionevent->is_actionable());
  378     }
  379 
  380     public function test_scorm_core_calendar_provide_event_action_no_time_specified() {
  381         $this->resetAfterTest();
  382 
  383         $this->setAdminUser();
  384 
  385         // Create a course.
  386         $course = $this->getDataGenerator()->create_course();
  387 
  388         // Create a scorm activity.
  389         $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id));
  390 
  391         // Create a calendar event.
  392         $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN);
  393 
  394         // Only students see scorm events.
  395         $this->setUser($this->student);
  396 
  397         // Create an action factory.
  398         $factory = new \core_calendar\action_factory();
  399 
  400         // Decorate action event.
  401         $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory);
  402 
  403         // Confirm the event was decorated.
  404         $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
  405         $this->assertEquals(get_string('enter', 'scorm'), $actionevent->get_name());
  406         $this->assertInstanceOf('moodle_url', $actionevent->get_url());
  407         $this->assertEquals(1, $actionevent->get_item_count());
  408         $this->assertTrue($actionevent->is_actionable());
  409     }
  410 
  411     public function test_scorm_core_calendar_provide_event_action_already_completed() {
  412         $this->resetAfterTest();
  413         set_config('enablecompletion', 1);
  414         $this->setAdminUser();
  415 
  416         // Create the activity.
  417         $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
  418         $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id),
  419             array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
  420 
  421         // Get some additional data.
  422         $cm = get_coursemodule_from_instance('scorm', $scorm->id);
  423 
  424         // Create a calendar event.
  425         $event = $this->create_action_event($course->id, $scorm->id,
  426             \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
  427 
  428         // Mark the activity as completed.
  429         $completion = new completion_info($course);
  430         $completion->set_module_viewed($cm);
  431 
  432         // Create an action factory.
  433         $factory = new \core_calendar\action_factory();
  434 
  435         // Decorate action event.
  436         $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory);
  437 
  438         // Ensure result was null.
  439         $this->assertNull($actionevent);
  440     }
  441 
  442     public function test_scorm_core_calendar_provide_event_action_already_completed_for_user() {
  443         $this->resetAfterTest();
  444         set_config('enablecompletion', 1);
  445         $this->setAdminUser();
  446 
  447         // Create the activity.
  448         $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
  449         $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id),
  450             array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS));
  451 
  452         // Enrol a student in the course.
  453         $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
  454 
  455         // Get some additional data.
  456         $cm = get_coursemodule_from_instance('scorm', $scorm->id);
  457 
  458         // Create a calendar event.
  459         $event = $this->create_action_event($course->id, $scorm->id,
  460             \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED);
  461 
  462         // Mark the activity as completed for the student.
  463         $completion = new completion_info($course);
  464         $completion->set_module_viewed($cm, $student->id);
  465 
  466         // Create an action factory.
  467         $factory = new \core_calendar\action_factory();
  468 
  469         // Decorate action event for the student.
  470         $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory, $student->id);
  471 
  472         // Ensure result was null.
  473         $this->assertNull($actionevent);
  474     }
  475 
  476     /**
  477      * Creates an action event.
  478      *
  479      * @param int $courseid
  480      * @param int $instanceid The data id.
  481      * @param string $eventtype The event type. eg. DATA_EVENT_TYPE_OPEN.
  482      * @param int|null $timestart The start timestamp for the event
  483      * @return bool|calendar_event
  484      */
  485     private function create_action_event($courseid, $instanceid, $eventtype, $timestart = null) {
  486         $event = new stdClass();
  487         $event->name = 'Calendar event';
  488         $event->modulename = 'scorm';
  489         $event->courseid = $courseid;
  490         $event->instance = $instanceid;
  491         $event->type = CALENDAR_EVENT_TYPE_ACTION;
  492         $event->eventtype = $eventtype;
  493         $event->eventtype = $eventtype;
  494 
  495         if ($timestart) {
  496             $event->timestart = $timestart;
  497         } else {
  498             $event->timestart = time();
  499         }
  500 
  501         return calendar_event::create($event);
  502     }
  503 
  504     /**
  505      * Test the callback responsible for returning the completion rule descriptions.
  506      * This function should work given either an instance of the module (cm_info), such as when checking the active rules,
  507      * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type.
  508      */
  509     public function test_mod_scorm_completion_get_active_rule_descriptions() {
  510         $this->resetAfterTest();
  511         $this->setAdminUser();
  512 
  513         // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't.
  514         $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]);
  515         $scorm1 = $this->getDataGenerator()->create_module('scorm', [
  516             'course' => $course->id,
  517             'completion' => 2,
  518             'completionstatusrequired' => 6,
  519             'completionscorerequired' => 5,
  520             'completionstatusallscos' => 1
  521         ]);
  522         $scorm2 = $this->getDataGenerator()->create_module('scorm', [
  523             'course' => $course->id,
  524             'completion' => 2,
  525             'completionstatusrequired' => null,
  526             'completionscorerequired' => null,
  527             'completionstatusallscos' => null
  528         ]);
  529         $cm1 = cm_info::create(get_coursemodule_from_instance('scorm', $scorm1->id));
  530         $cm2 = cm_info::create(get_coursemodule_from_instance('scorm', $scorm2->id));
  531 
  532         // Data for the stdClass input type.
  533         // This type of input would occur when checking the default completion rules for an activity type, where we don't have
  534         // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info.
  535         $moddefaults = new stdClass();
  536         $moddefaults->customdata = ['customcompletionrules' => [
  537             'completionstatusrequired' => 6,
  538             'completionscorerequired' => 5,
  539             'completionstatusallscos' => 1
  540         ]];
  541         $moddefaults->completion = 2;
  542 
  543         // Determine the selected statuses using a bitwise operation.
  544         $cvalues = array();
  545         foreach (scorm_status_options(true) as $key => $value) {
  546             if (($scorm1->completionstatusrequired & $key) == $key) {
  547                 $cvalues[] = $value;
  548             }
  549         }
  550         $statusstring = implode(', ', $cvalues);
  551 
  552         $activeruledescriptions = [
  553             get_string('completionstatusrequireddesc', 'scorm', $statusstring),
  554             get_string('completionscorerequireddesc', 'scorm', $scorm1->completionscorerequired),
  555             get_string('completionstatusallscos', 'scorm'),
  556         ];
  557         $this->assertEquals(mod_scorm_get_completion_active_rule_descriptions($cm1), $activeruledescriptions);
  558         $this->assertEquals(mod_scorm_get_completion_active_rule_descriptions($cm2), []);
  559         $this->assertEquals(mod_scorm_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions);
  560         $this->assertEquals(mod_scorm_get_completion_active_rule_descriptions(new stdClass()), []);
  561     }
  562 
  563     /**
  564      * An unkown event type should not change the scorm instance.
  565      */
  566     public function test_mod_scorm_core_calendar_event_timestart_updated_unknown_event() {
  567         global $CFG, $DB;
  568         require_once($CFG->dirroot . "/calendar/lib.php");
  569 
  570         $this->resetAfterTest(true);
  571         $this->setAdminUser();
  572         $generator = $this->getDataGenerator();
  573         $course = $generator->create_course();
  574         $scormgenerator = $generator->get_plugin_generator('mod_scorm');
  575         $timeopen = time();
  576         $timeclose = $timeopen + DAYSECS;
  577         $scorm = $scormgenerator->create_instance(['course' => $course->id]);
  578         $scorm->timeopen = $timeopen;
  579         $scorm->timeclose = $timeclose;
  580         $DB->update_record('scorm', $scorm);
  581 
  582         // Create a valid event.
  583         $event = new \calendar_event([
  584             'name' => 'Test event',
  585             'description' => '',
  586             'format' => 1,
  587             'courseid' => $course->id,
  588             'groupid' => 0,
  589             'userid' => 2,
  590             'modulename' => 'scorm',
  591             'instance' => $scorm->id,
  592             'eventtype' => SCORM_EVENT_TYPE_OPEN . "SOMETHING ELSE",
  593             'timestart' => 1,
  594             'timeduration' => 86400,
  595             'visible' => 1
  596         ]);
  597 
  598         mod_scorm_core_calendar_event_timestart_updated($event, $scorm);
  599 
  600         $scorm = $DB->get_record('scorm', ['id' => $scorm->id]);
  601         $this->assertEquals($timeopen, $scorm->timeopen);
  602         $this->assertEquals($timeclose, $scorm->timeclose);
  603     }
  604 
  605     /**
  606      * A SCORM_EVENT_TYPE_OPEN event should update the timeopen property of
  607      * the scorm activity.
  608      */
  609     public function test_mod_scorm_core_calendar_event_timestart_updated_open_event() {
  610         global $CFG, $DB;
  611         require_once($CFG->dirroot . "/calendar/lib.php");
  612 
  613         $this->resetAfterTest(true);
  614         $this->setAdminUser();
  615         $generator = $this->getDataGenerator();
  616         $course = $generator->create_course();
  617         $scormgenerator = $generator->get_plugin_generator('mod_scorm');
  618         $timeopen = time();
  619         $timeclose = $timeopen + DAYSECS;
  620         $timemodified = 1;
  621         $newtimeopen = $timeopen - DAYSECS;
  622         $scorm = $scormgenerator->create_instance(['course' => $course->id]);
  623         $scorm->timeopen = $timeopen;
  624         $scorm->timeclose = $timeclose;
  625         $scorm->timemodified = $timemodified;
  626         $DB->update_record('scorm', $scorm);
  627 
  628         // Create a valid event.
  629         $event = new \calendar_event([
  630             'name' => 'Test event',
  631             'description' => '',
  632             'format' => 1,
  633             'courseid' => $course->id,
  634             'groupid' => 0,
  635             'userid' => 2,
  636             'modulename' => 'scorm',
  637             'instance' => $scorm->id,
  638             'eventtype' => SCORM_EVENT_TYPE_OPEN,
  639             'timestart' => $newtimeopen,
  640             'timeduration' => 86400,
  641             'visible' => 1
  642         ]);
  643 
  644         // Trigger and capture the event when adding a contact.
  645         $sink = $this->redirectEvents();
  646 
  647         mod_scorm_core_calendar_event_timestart_updated($event, $scorm);
  648 
  649         $triggeredevents = $sink->get_events();
  650         $moduleupdatedevents = array_filter($triggeredevents, function($e) {
  651             return is_a($e, 'core\event\course_module_updated');
  652         });
  653 
  654         $scorm = $DB->get_record('scorm', ['id' => $scorm->id]);
  655         // Ensure the timeopen property matches the event timestart.
  656         $this->assertEquals($newtimeopen, $scorm->timeopen);
  657         // Ensure the timeclose isn't changed.
  658         $this->assertEquals($timeclose, $scorm->timeclose);
  659         // Ensure the timemodified property has been changed.
  660         $this->assertNotEquals($timemodified, $scorm->timemodified);
  661         // Confirm that a module updated event is fired when the module
  662         // is changed.
  663         $this->assertNotEmpty($moduleupdatedevents);
  664     }
  665 
  666     /**
  667      * A SCORM_EVENT_TYPE_CLOSE event should update the timeclose property of
  668      * the scorm activity.
  669      */
  670     public function test_mod_scorm_core_calendar_event_timestart_updated_close_event() {
  671         global $CFG, $DB;
  672         require_once($CFG->dirroot . "/calendar/lib.php");
  673 
  674         $this->resetAfterTest(true);
  675         $this->setAdminUser();
  676         $generator = $this->getDataGenerator();
  677         $course = $generator->create_course();
  678         $scormgenerator = $generator->get_plugin_generator('mod_scorm');
  679         $timeopen = time();
  680         $timeclose = $timeopen + DAYSECS;
  681         $timemodified = 1;
  682         $newtimeclose = $timeclose + DAYSECS;
  683         $scorm = $scormgenerator->create_instance(['course' => $course->id]);
  684         $scorm->timeopen = $timeopen;
  685         $scorm->timeclose = $timeclose;
  686         $scorm->timemodified = $timemodified;
  687         $DB->update_record('scorm', $scorm);
  688 
  689         // Create a valid event.
  690         $event = new \calendar_event([
  691             'name' => 'Test event',
  692             'description' => '',
  693             'format' => 1,
  694             'courseid' => $course->id,
  695             'groupid' => 0,
  696             'userid' => 2,
  697             'modulename' => 'scorm',
  698             'instance' => $scorm->id,
  699             'eventtype' => SCORM_EVENT_TYPE_CLOSE,
  700             'timestart' => $newtimeclose,
  701             'timeduration' => 86400,
  702             'visible' => 1
  703         ]);
  704 
  705         // Trigger and capture the event when adding a contact.
  706         $sink = $this->redirectEvents();
  707 
  708         mod_scorm_core_calendar_event_timestart_updated($event, $scorm);
  709 
  710         $triggeredevents = $sink->get_events();
  711         $moduleupdatedevents = array_filter($triggeredevents, function($e) {
  712             return is_a($e, 'core\event\course_module_updated');
  713         });
  714 
  715         $scorm = $DB->get_record('scorm', ['id' => $scorm->id]);
  716         // Ensure the timeclose property matches the event timestart.
  717         $this->assertEquals($newtimeclose, $scorm->timeclose);
  718         // Ensure the timeopen isn't changed.
  719         $this->assertEquals($timeopen, $scorm->timeopen);
  720         // Ensure the timemodified property has been changed.
  721         $this->assertNotEquals($timemodified, $scorm->timemodified);
  722         // Confirm that a module updated event is fired when the module
  723         // is changed.
  724         $this->assertNotEmpty($moduleupdatedevents);
  725     }
  726 
  727     /**
  728      * An unkown event type should not have any limits
  729      */
  730     public function test_mod_scorm_core_calendar_get_valid_event_timestart_range_unknown_event() {
  731         global $CFG, $DB;
  732         require_once($CFG->dirroot . "/calendar/lib.php");
  733 
  734         $this->resetAfterTest(true);
  735         $this->setAdminUser();
  736         $generator = $this->getDataGenerator();
  737         $course = $generator->create_course();
  738         $timeopen = time();
  739         $timeclose = $timeopen + DAYSECS;
  740         $scorm = new \stdClass();
  741         $scorm->timeopen = $timeopen;
  742         $scorm->timeclose = $timeclose;
  743 
  744         // Create a valid event.
  745         $event = new \calendar_event([
  746             'name' => 'Test event',
  747             'description' => '',
  748             'format' => 1,
  749             'courseid' => $course->id,
  750             'groupid' => 0,
  751             'userid' => 2,
  752             'modulename' => 'scorm',
  753             'instance' => 1,
  754             'eventtype' => SCORM_EVENT_TYPE_OPEN . "SOMETHING ELSE",
  755             'timestart' => 1,
  756             'timeduration' => 86400,
  757             'visible' => 1
  758         ]);
  759 
  760         list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm);
  761         $this->assertNull($min);
  762         $this->assertNull($max);
  763     }
  764 
  765     /**
  766      * The open event should be limited by the scorm's timeclose property, if it's set.
  767      */
  768     public function test_mod_scorm_core_calendar_get_valid_event_timestart_range_open_event() {
  769         global $CFG, $DB;
  770         require_once($CFG->dirroot . "/calendar/lib.php");
  771 
  772         $this->resetAfterTest(true);
  773         $this->setAdminUser();
  774         $generator = $this->getDataGenerator();
  775         $course = $generator->create_course();
  776         $timeopen = time();
  777         $timeclose = $timeopen + DAYSECS;
  778         $scorm = new \stdClass();
  779         $scorm->timeopen = $timeopen;
  780         $scorm->timeclose = $timeclose;
  781 
  782         // Create a valid event.
  783         $event = new \calendar_event([
  784             'name' => 'Test event',
  785             'description' => '',
  786             'format' => 1,
  787             'courseid' => $course->id,
  788             'groupid' => 0,
  789             'userid' => 2,
  790             'modulename' => 'scorm',
  791             'instance' => 1,
  792             'eventtype' => SCORM_EVENT_TYPE_OPEN,
  793             'timestart' => 1,
  794             'timeduration' => 86400,
  795             'visible' => 1
  796         ]);
  797 
  798         // The max limit should be bounded by the timeclose value.
  799         list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm);
  800 
  801         $this->assertNull($min);
  802         $this->assertEquals($timeclose, $max[0]);
  803 
  804         // No timeclose value should result in no upper limit.
  805         $scorm->timeclose = 0;
  806         list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm);
  807 
  808         $this->assertNull($min);
  809         $this->assertNull($max);
  810     }
  811 
  812     /**
  813      * The close event should be limited by the scorm's timeopen property, if it's set.
  814      */
  815     public function test_mod_scorm_core_calendar_get_valid_event_timestart_range_close_event() {
  816         global $CFG, $DB;
  817         require_once($CFG->dirroot . "/calendar/lib.php");
  818 
  819         $this->resetAfterTest(true);
  820         $this->setAdminUser();
  821         $generator = $this->getDataGenerator();
  822         $course = $generator->create_course();
  823         $timeopen = time();
  824         $timeclose = $timeopen + DAYSECS;
  825         $scorm = new \stdClass();
  826         $scorm->timeopen = $timeopen;
  827         $scorm->timeclose = $timeclose;
  828 
  829         // Create a valid event.
  830         $event = new \calendar_event([
  831             'name' => 'Test event',
  832             'description' => '',
  833             'format' => 1,
  834             'courseid' => $course->id,
  835             'groupid' => 0,
  836             'userid' => 2,
  837             'modulename' => 'scorm',
  838             'instance' => 1,
  839             'eventtype' => SCORM_EVENT_TYPE_CLOSE,
  840             'timestart' => 1,
  841             'timeduration' => 86400,
  842             'visible' => 1
  843         ]);
  844 
  845         // The max limit should be bounded by the timeclose value.
  846         list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm);
  847 
  848         $this->assertEquals($timeopen, $min[0]);
  849         $this->assertNull($max);
  850 
  851         // No timeclose value should result in no upper limit.
  852         $scorm->timeopen = 0;
  853         list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm);
  854 
  855         $this->assertNull($min);
  856         $this->assertNull($max);
  857     }
  858 
  859     /**
  860      * A user who does not have capabilities to add events to the calendar should be able to create a SCORM.
  861      */
  862     public function test_creation_with_no_calendar_capabilities() {
  863         $this->resetAfterTest();
  864         $course = self::getDataGenerator()->create_course();
  865         $context = context_course::instance($course->id);
  866         $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher');
  867         $roleid = self::getDataGenerator()->create_role();
  868         self::getDataGenerator()->role_assign($roleid, $user->id, $context->id);
  869         assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true);
  870         $generator = self::getDataGenerator()->get_plugin_generator('mod_scorm');
  871         // Create an instance as a user without the calendar capabilities.
  872         $this->setUser($user);
  873         $time = time();
  874         $params = array(
  875             'course' => $course->id,
  876             'timeopen' => $time + 200,
  877             'timeclose' => $time + 2000,
  878         );
  879         $generator->create_instance($params);
  880     }
  881 }