Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
   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   * Base class for unit tests for mod_scorm.
  19   *
  20   * @package    mod_scorm
  21   * @category   test
  22   * @copyright  2018 Sara Arjona <sara@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  use mod_scorm\privacy\provider;
  29  use core_privacy\local\request\approved_contextlist;
  30  use core_privacy\local\request\approved_userlist;
  31  use core_privacy\local\request\writer;
  32  use core_privacy\tests\provider_testcase;
  33  
  34  /**
  35   * Unit tests for mod\scorm\classes\privacy\provider.php
  36   *
  37   * @copyright  2018 Sara Arjona <sara@moodle.com>
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class mod_scorm_testcase extends provider_testcase {
  41  
  42      /** @var stdClass User without any AICC/SCORM attempt. */
  43      protected $student0;
  44  
  45      /** @var stdClass User with some AICC/SCORM attempt. */
  46      protected $student1;
  47  
  48      /** @var stdClass User with some AICC/SCORM attempt. */
  49      protected $student2;
  50  
  51      /** @var context context_module of the SCORM activity. */
  52      protected $context;
  53  
  54      /**
  55       * Test getting the context for the user ID related to this plugin.
  56       */
  57      public function test_get_contexts_for_userid() {
  58          $this->resetAfterTest(true);
  59          $this->setAdminUser();
  60          $this->scorm_setup_test_scenario_data();
  61  
  62          // The student0 hasn't any attempt.
  63          $contextlist = provider::get_contexts_for_userid($this->student0->id);
  64          $this->assertCount(0, (array) $contextlist->get_contextids());
  65  
  66          // The student1 has data in the SCORM context.
  67          $contextlist = provider::get_contexts_for_userid($this->student1->id);
  68          $this->assertCount(1, (array) $contextlist->get_contextids());
  69          $this->assertContains($this->context->id, $contextlist->get_contextids());
  70      }
  71  
  72      /**
  73       * Test getting the user IDs for the context related to this plugin.
  74       */
  75      public function test_get_users_in_context() {
  76          $this->resetAfterTest(true);
  77          $this->setAdminUser();
  78          $this->scorm_setup_test_scenario_data();
  79          $component = 'mod_scorm';
  80  
  81          $userlist = new \core_privacy\local\request\userlist($this->context, $component);
  82          provider::get_users_in_context($userlist);
  83  
  84          // Students 1 and 2 have attempts in the SCORM context, student 0 does not.
  85          $this->assertCount(2, $userlist);
  86  
  87          $expected = [$this->student1->id, $this->student2->id];
  88          $actual = $userlist->get_userids();
  89          sort($expected);
  90          sort($actual);
  91          $this->assertEquals($expected, $actual);
  92      }
  93  
  94      /**
  95       * Test that data is exported correctly for this plugin.
  96       */
  97      public function test_export_user_data() {
  98          $this->resetAfterTest(true);
  99          $this->setAdminUser();
 100          $this->scorm_setup_test_scenario_data();
 101  
 102          // Validate exported data for student0 (without any AICC/SCORM attempt).
 103          $this->setUser($this->student0);
 104          $writer = writer::with_context($this->context);
 105  
 106          $this->export_context_data_for_user($this->student0->id, $this->context, 'mod_scorm');
 107          $subcontextattempt1 = [
 108              get_string('myattempts', 'scorm'),
 109              get_string('attempt', 'scorm'). " 1"
 110          ];
 111          $subcontextaicc = [
 112              get_string('myaiccsessions', 'scorm')
 113          ];
 114          $data = $writer->get_data($subcontextattempt1);
 115          $this->assertEmpty($data);
 116          $data = $writer->get_data($subcontextaicc);
 117          $this->assertEmpty($data);
 118  
 119          // Validate exported data for student1.
 120          writer::reset();
 121          $this->setUser($this->student1);
 122          $writer = writer::with_context($this->context);
 123          $this->assertFalse($writer->has_any_data());
 124          $this->export_context_data_for_user($this->student1->id, $this->context, 'mod_scorm');
 125  
 126          $data = $writer->get_data([]);
 127          $this->assertEquals('SCORM1', $data->name);
 128  
 129          $data = $writer->get_data($subcontextattempt1);
 130          $this->assertCount(1, (array) $data);
 131          $this->assertCount(2, (array) reset($data));
 132          $subcontextattempt2 = [
 133              get_string('myattempts', 'scorm'),
 134              get_string('attempt', 'scorm'). " 2"
 135          ];
 136          $data = $writer->get_data($subcontextattempt2);
 137          $this->assertCount(2, (array) reset($data));
 138          // The student1 has only 2 scoes_track attempts.
 139          $subcontextattempt3 = [
 140              get_string('myattempts', 'scorm'),
 141              get_string('attempt', 'scorm'). " 3"
 142          ];
 143          $data = $writer->get_data($subcontextattempt3);
 144          $this->assertEmpty($data);
 145          // The student1 has only 1 aicc_session.
 146          $data = $writer->get_data($subcontextaicc);
 147          $this->assertCount(1, (array) $data);
 148      }
 149  
 150      /**
 151       * Test for provider::delete_data_for_all_users_in_context().
 152       */
 153      public function test_delete_data_for_all_users_in_context() {
 154          global $DB;
 155  
 156          $this->resetAfterTest(true);
 157          $this->setAdminUser();
 158          $this->scorm_setup_test_scenario_data();
 159  
 160          // Before deletion, we should have 8 entries in the scorm_scoes_track table.
 161          $count = $DB->count_records('scorm_scoes_track');
 162          $this->assertEquals(8, $count);
 163          // Before deletion, we should have 4 entries in the scorm_aicc_session table.
 164          $count = $DB->count_records('scorm_aicc_session');
 165          $this->assertEquals(4, $count);
 166  
 167          // Delete data based on the context.
 168          provider::delete_data_for_all_users_in_context($this->context);
 169  
 170          // After deletion, the scorm_scoes_track entries should have been deleted.
 171          $count = $DB->count_records('scorm_scoes_track');
 172          $this->assertEquals(0, $count);
 173          // After deletion, the scorm_aicc_session entries should have been deleted.
 174          $count = $DB->count_records('scorm_aicc_session');
 175          $this->assertEquals(0, $count);
 176      }
 177  
 178      /**
 179       * Test for provider::delete_data_for_user().
 180       */
 181      public function test_delete_data_for_user() {
 182          global $DB;
 183  
 184          $this->resetAfterTest(true);
 185          $this->setAdminUser();
 186          $this->scorm_setup_test_scenario_data();
 187  
 188          // Before deletion, we should have 8 entries in the scorm_scoes_track table.
 189          $count = $DB->count_records('scorm_scoes_track');
 190          $this->assertEquals(8, $count);
 191          // Before deletion, we should have 4 entries in the scorm_aicc_session table.
 192          $count = $DB->count_records('scorm_aicc_session');
 193          $this->assertEquals(4, $count);
 194  
 195          $approvedcontextlist = new approved_contextlist($this->student1, 'scorm', [$this->context->id]);
 196          provider::delete_data_for_user($approvedcontextlist);
 197  
 198          // After deletion, the scorm_scoes_track entries for the first student should have been deleted.
 199          $count = $DB->count_records('scorm_scoes_track', ['userid' => $this->student1->id]);
 200          $this->assertEquals(0, $count);
 201          $count = $DB->count_records('scorm_scoes_track');
 202          $this->assertEquals(4, $count);
 203          // After deletion, the scorm_aicc_session entries for the first student should have been deleted.
 204          $count = $DB->count_records('scorm_aicc_session', ['userid' => $this->student1->id]);
 205          $this->assertEquals(0, $count);
 206          $count = $DB->count_records('scorm_aicc_session');
 207          $this->assertEquals(2, $count);
 208  
 209          // Confirm that the SCORM hasn't been removed.
 210          $scormcount = $DB->get_records('scorm');
 211          $this->assertCount(1, (array) $scormcount);
 212  
 213          // Delete scoes_track for student0 (nothing has to be removed).
 214          $approvedcontextlist = new approved_contextlist($this->student0, 'scorm', [$this->context->id]);
 215          provider::delete_data_for_user($approvedcontextlist);
 216          $count = $DB->count_records('scorm_scoes_track');
 217          $this->assertEquals(4, $count);
 218          $count = $DB->count_records('scorm_aicc_session');
 219          $this->assertEquals(2, $count);
 220      }
 221  
 222      /**
 223       * Test for provider::delete_data_for_users().
 224       */
 225      public function test_delete_data_for_users() {
 226          global $DB;
 227          $component = 'mod_scorm';
 228  
 229          $this->resetAfterTest(true);
 230          $this->setAdminUser();
 231          $this->scorm_setup_test_scenario_data();
 232  
 233          // Before deletion, we should have 8 entries in the scorm_scoes_track table.
 234          $count = $DB->count_records('scorm_scoes_track');
 235          $this->assertEquals(8, $count);
 236          // Before deletion, we should have 4 entries in the scorm_aicc_session table.
 237          $count = $DB->count_records('scorm_aicc_session');
 238          $this->assertEquals(4, $count);
 239  
 240          // Delete only student 1's data, retain student 2's data.
 241          $approveduserids = [$this->student1->id];
 242          $approvedlist = new approved_userlist($this->context, $component, $approveduserids);
 243          provider::delete_data_for_users($approvedlist);
 244  
 245          // After deletion, the scorm_scoes_track entries for the first student should have been deleted.
 246          $count = $DB->count_records('scorm_scoes_track', ['userid' => $this->student1->id]);
 247          $this->assertEquals(0, $count);
 248          $count = $DB->count_records('scorm_scoes_track');
 249          $this->assertEquals(4, $count);
 250  
 251          // After deletion, the scorm_aicc_session entries for the first student should have been deleted.
 252          $count = $DB->count_records('scorm_aicc_session', ['userid' => $this->student1->id]);
 253          $this->assertEquals(0, $count);
 254          $count = $DB->count_records('scorm_aicc_session');
 255          $this->assertEquals(2, $count);
 256  
 257          // Confirm that the SCORM hasn't been removed.
 258          $scormcount = $DB->get_records('scorm');
 259          $this->assertCount(1, (array) $scormcount);
 260  
 261          // Delete scoes_track for student0 (nothing has to be removed).
 262          $approveduserids = [$this->student0->id];
 263          $approvedlist = new approved_userlist($this->context, $component, $approveduserids);
 264          provider::delete_data_for_users($approvedlist);
 265  
 266          $count = $DB->count_records('scorm_scoes_track');
 267          $this->assertEquals(4, $count);
 268          $count = $DB->count_records('scorm_aicc_session');
 269          $this->assertEquals(2, $count);
 270      }
 271  
 272      /**
 273       * Helper function to setup 3 users and 2 SCORM attempts for student1 and student2.
 274       * $this->student0 is always created without any attempt.
 275       */
 276      protected function scorm_setup_test_scenario_data() {
 277          global $DB;
 278  
 279          set_config('allowaicchacp', 1, 'scorm');
 280  
 281          // Setup test data.
 282          $course = $this->getDataGenerator()->create_course();
 283          $params = array('course' => $course->id, 'name' => 'SCORM1');
 284          $scorm = $this->getDataGenerator()->create_module('scorm', $params);
 285          $this->context = \context_module::instance($scorm->cmid);
 286  
 287          // Users enrolments.
 288          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 289  
 290          // Create student0 withot any SCORM attempt.
 291          $this->student0 = self::getDataGenerator()->create_user();
 292          $this->getDataGenerator()->enrol_user($this->student0->id, $course->id, $studentrole->id, 'manual');
 293  
 294          // Create student1 with 2 SCORM attempts and 1 AICC session.
 295          $this->student1 = self::getDataGenerator()->create_user();
 296          $this->getDataGenerator()->enrol_user($this->student1->id, $course->id, $studentrole->id, 'manual');
 297          static::scorm_insert_attempt($scorm, $this->student1->id, 1);
 298          static::scorm_insert_attempt($scorm, $this->student1->id, 2);
 299  
 300          // Create student2 with 2 SCORM attempts and 1 AICC session.
 301          $this->student2 = self::getDataGenerator()->create_user();
 302          $this->getDataGenerator()->enrol_user($this->student2->id, $course->id, $studentrole->id, 'manual');
 303          static::scorm_insert_attempt($scorm, $this->student2->id, 1);
 304          static::scorm_insert_attempt($scorm, $this->student2->id, 2);
 305      }
 306  
 307      /**
 308       * Create a SCORM attempt.
 309       *
 310       * @param  object $scorm SCORM activity.
 311       * @param  int $userid  Userid who is doing the attempt.
 312       * @param  int $attempt Number of attempt.
 313       */
 314      protected function scorm_insert_attempt($scorm, $userid, $attempt) {
 315          global $DB;
 316  
 317          $newattempt = 'on';
 318          $mode = 'normal';
 319          scorm_check_mode($scorm, $newattempt, $attempt, $userid, $mode);
 320          $scoes = scorm_get_scoes($scorm->id);
 321          $sco = array_pop($scoes);
 322          scorm_insert_track($userid, $scorm->id, $sco->id, $attempt, 'cmi.core.lesson_status', 'completed');
 323          scorm_insert_track($userid, $scorm->id, $sco->id, $attempt, 'cmi.score.min', '0');
 324          $now = time();
 325          $hacpsession = [
 326              'scormid' => $scorm->id,
 327              'attempt' => $attempt,
 328              'hacpsession' => random_string(20),
 329              'userid' => $userid,
 330              'timecreated' => $now,
 331              'timemodified' => $now
 332          ];
 333          $DB->insert_record('scorm_aicc_session', $hacpsession);
 334      }
 335  }