Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 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.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 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   * mod_h5pactivity generator tests
  19   *
  20   * @package    mod_h5pactivity
  21   * @category   test
  22   * @copyright  2020 Ferran Recio <ferran@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  use mod_h5pactivity\local\manager;
  27  
  28  /**
  29   * Genarator tests class for mod_h5pactivity.
  30   *
  31   * @package    mod_h5pactivity
  32   * @category   test
  33   * @copyright  2020 Ferran Recio <ferran@moodle.com>
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class mod_h5pactivity_generator_testcase extends advanced_testcase {
  37  
  38      /**
  39       * Test on H5P activity creation.
  40       */
  41      public function test_create_instance() {
  42          global $DB, $CFG, $USER;
  43          $this->resetAfterTest();
  44          $this->setAdminUser();
  45  
  46          $course = $this->getDataGenerator()->create_course();
  47  
  48          // Create one activity.
  49          $this->assertFalse($DB->record_exists('h5pactivity', ['course' => $course->id]));
  50          $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
  51          $records = $DB->get_records('h5pactivity', ['course' => $course->id], 'id');
  52          $this->assertEquals(15, $activity->displayoptions);
  53          $this->assertEquals(1, count($records));
  54          $this->assertTrue(array_key_exists($activity->id, $records));
  55  
  56          // Create a second one with different name and dusplay options.
  57          $params = [
  58              'course' => $course->id, 'name' => 'Another h5pactivity', 'displayoptions' => 6,
  59              'enabletracking' => 0, 'grademethod' => manager::GRADELASTATTEMPT,
  60          ];
  61          $activity = $this->getDataGenerator()->create_module('h5pactivity', $params);
  62          $records = $DB->get_records('h5pactivity', ['course' => $course->id], 'id');
  63          $this->assertEquals(6, $activity->displayoptions);
  64          $this->assertEquals(0, $activity->enabletracking);
  65          $this->assertEquals(manager::GRADELASTATTEMPT, $activity->grademethod);
  66          $this->assertEquals(manager::REVIEWCOMPLETION, $activity->reviewmode);
  67          $this->assertEquals(2, count($records));
  68          $this->assertEquals('Another h5pactivity', $records[$activity->id]->name);
  69  
  70          // Examples of specifying the package file (do not validate anything, just check for exceptions).
  71          // 1. As path to the file in filesystem.
  72          $params = [
  73              'course' => $course->id,
  74              'packagefilepath' => $CFG->dirroot.'/h5p/tests/fixtures/filltheblanks.h5p'
  75          ];
  76          $activity = $this->getDataGenerator()->create_module('h5pactivity', $params);
  77  
  78          // 2. As file draft area id.
  79          $fs = get_file_storage();
  80          $params = [
  81              'course' => $course->id,
  82              'packagefile' => file_get_unused_draft_itemid()
  83          ];
  84          $usercontext = context_user::instance($USER->id);
  85          $filerecord = ['component' => 'user', 'filearea' => 'draft',
  86                  'contextid' => $usercontext->id, 'itemid' => $params['packagefile'],
  87                  'filename' => 'singlescobasic.zip', 'filepath' => '/'];
  88          $filepath = $CFG->dirroot.'/h5p/tests/fixtures/filltheblanks.h5p';
  89          $fs->create_file_from_pathname($filerecord, $filepath);
  90          $activity = $this->getDataGenerator()->create_module('h5pactivity', $params);
  91      }
  92  
  93      /**
  94       * Test that a new H5P activity cannot be generated without a valid file
  95       * other user.
  96       */
  97      public function test_create_file_exception() {
  98          global $CFG;
  99          $this->resetAfterTest();
 100          $this->setAdminUser();
 101  
 102          $course = $this->getDataGenerator()->create_course();
 103  
 104          // Testing generator exceptions.
 105          $params = [
 106              'course' => $course->id,
 107              'packagefilepath' => $CFG->dirroot.'/h5p/tests/fixtures/wrong_file_.xxx'
 108          ];
 109          $this->expectException(coding_exception::class);
 110          $activity = $this->getDataGenerator()->create_module('h5pactivity', $params);
 111      }
 112  
 113      /**
 114       * Test to create H5P attempts
 115       *
 116       * @dataProvider create_attempt_data
 117       *
 118       * @param array $tracks the attempt tracks objects
 119       * @param int $attempts the final registered attempts
 120       * @param int $results the final registered attempts results
 121       * @param bool $exception if an exception is expected
 122       *
 123       */
 124      public function test_create_attempt(array $tracks, int $attempts, int $results, bool $exception) {
 125          global $DB;
 126          $this->resetAfterTest();
 127          $this->setAdminUser();
 128  
 129          $course = $this->getDataGenerator()->create_course();
 130          $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
 131          $user = $this->getDataGenerator()->create_and_enrol($course, 'student');
 132  
 133          $this->assertEquals(0, $DB->count_records('h5pactivity_attempts'));
 134          $this->assertEquals(0, $DB->count_records('h5pactivity_attempts_results'));
 135  
 136          if ($exception) {
 137              $this->expectException(Exception::class);
 138          }
 139  
 140          foreach ($tracks as $track) {
 141              $attemptinfo = [
 142                  'userid' => $user->id,
 143                  'h5pactivityid' => $activity->id,
 144                  'attempt' => $track['attempt'],
 145                  'interactiontype' => $track['interactiontype'],
 146                  'rawscore' => $track['rawscore'],
 147                  'maxscore' => $track['maxscore'],
 148                  'duration' => $track['duration'],
 149                  'completion' => $track['completion'],
 150                  'success' => $track['success'],
 151              ];
 152  
 153              $generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity');
 154              $generator->create_attempt($attemptinfo);
 155  
 156              $this->assert_attempt_matches_info($attemptinfo);
 157          }
 158  
 159          $this->assertEquals($attempts, $DB->count_records('h5pactivity_attempts'));
 160          $this->assertEquals($results, $DB->count_records('h5pactivity_attempts_results'));
 161      }
 162  
 163      /**
 164       * Data provider for create attempt test.
 165       *
 166       * @return array
 167       */
 168      public function create_attempt_data(): array {
 169          return [
 170              'Compound statement' => [
 171                  [
 172                      [
 173                          'interactiontype' => 'compound', 'attempt' => 1, 'rawscore' => 2,
 174                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 175                      ],
 176                  ], 1, 1, false,
 177              ],
 178              'Choice statement' => [
 179                  [
 180                      [
 181                          'interactiontype' => 'choice', 'attempt' => 1, 'rawscore' => 2,
 182                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 183                      ],
 184                  ], 1, 1, false,
 185              ],
 186              'Matching statement' => [
 187                  [
 188                      [
 189                          'interactiontype' => 'matching', 'attempt' => 1, 'rawscore' => 2,
 190                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 191                      ],
 192                  ], 1, 1, false,
 193              ],
 194              'Fill-in statement' => [
 195                  [
 196                      [
 197                          'interactiontype' => 'fill-in', 'attempt' => 1, 'rawscore' => 2,
 198                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 199                      ],
 200                  ], 1, 1, false,
 201              ],
 202              'True-false statement' => [
 203                  [
 204                      [
 205                          'interactiontype' => 'true-false', 'attempt' => 1, 'rawscore' => 2,
 206                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 207                      ],
 208                  ], 1, 1, false,
 209              ],
 210              'Long-fill-in statement' => [
 211                  [
 212                      [
 213                          'interactiontype' => 'long-fill-in', 'attempt' => 1, 'rawscore' => 2,
 214                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 215                      ],
 216                  ], 1, 1, false,
 217              ],
 218              'Sequencing statement' => [
 219                  [
 220                      [
 221                          'interactiontype' => 'sequencing', 'attempt' => 1, 'rawscore' => 2,
 222                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 223                      ],
 224                  ], 1, 1, false,
 225              ],
 226              'Other statement' => [
 227                  [
 228                      [
 229                          'interactiontype' => 'other', 'attempt' => 1, 'rawscore' => 2,
 230                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 231                      ],
 232                  ], 1, 1, false,
 233              ],
 234              'Other statement' => [
 235                  [
 236                      [
 237                          'interactiontype' => 'other', 'attempt' => 1, 'rawscore' => 2,
 238                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 239                      ],
 240                  ], 1, 1, false,
 241              ],
 242              'No graded statement' => [
 243                  [
 244                      [
 245                          'interactiontype' => 'other', 'attempt' => 1, 'rawscore' => 0,
 246                          'maxscore' => 0, 'duration' => 1, 'completion' => 1, 'success' => 0
 247                      ],
 248                  ], 1, 1, false,
 249              ],
 250              'Invalid statement type' => [
 251                  [
 252                      [
 253                          'interactiontype' => 'no-valid-statement-type', 'attempt' => 1, 'rawscore' => 2,
 254                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 255                      ],
 256                  ], 0, 0, true,
 257              ],
 258              'Adding a second statement to attempt' => [
 259                  [
 260                      [
 261                          'interactiontype' => 'true-false', 'attempt' => 1, 'rawscore' => 2,
 262                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 263                      ],
 264                      [
 265                          'interactiontype' => 'compound', 'attempt' => 1, 'rawscore' => 3,
 266                          'maxscore' => 3, 'duration' => 2, 'completion' => 1, 'success' => 0
 267                      ],
 268                  ], 1, 2, false,
 269              ],
 270              'Creating two attempts' => [
 271                  [
 272                      [
 273                          'interactiontype' => 'compound', 'attempt' => 1, 'rawscore' => 2,
 274                          'maxscore' => 2, 'duration' => 1, 'completion' => 1, 'success' => 0
 275                      ],
 276                      [
 277                          'interactiontype' => 'compound', 'attempt' => 2, 'rawscore' => 3,
 278                          'maxscore' => 3, 'duration' => 1, 'completion' => 1, 'success' => 0
 279                      ],
 280                  ], 2, 2, false,
 281              ],
 282          ];
 283      }
 284  
 285      /**
 286       * Insert track into attempt, creating the attempt if necessary.
 287       *
 288       * @param array $attemptinfo the attempt track information
 289       */
 290      private function assert_attempt_matches_info($attemptinfo): void {
 291          global $DB;
 292  
 293          $attempt = $DB->get_record('h5pactivity_attempts', [
 294              'userid' => $attemptinfo['userid'],
 295              'h5pactivityid' => $attemptinfo['h5pactivityid'],
 296              'attempt' => $attemptinfo['attempt'],
 297          ]);
 298          $this->assertEquals($attemptinfo['rawscore'], $attempt->rawscore);
 299          $this->assertEquals($attemptinfo['maxscore'], $attempt->maxscore);
 300          $this->assertEquals($attemptinfo['duration'], $attempt->duration);
 301          $this->assertEquals($attemptinfo['completion'], $attempt->completion);
 302          $this->assertEquals($attemptinfo['success'], $attempt->success);
 303  
 304          $track = $DB->get_record('h5pactivity_attempts_results', [
 305              'attemptid' => $attempt->id,
 306              'interactiontype' => $attemptinfo['interactiontype'],
 307          ]);
 308          $this->assertEquals($attemptinfo['rawscore'], $track->rawscore);
 309          $this->assertEquals($attemptinfo['maxscore'], $track->maxscore);
 310          $this->assertEquals($attemptinfo['duration'], $track->duration);
 311          $this->assertEquals($attemptinfo['completion'], $track->completion);
 312          $this->assertEquals($attemptinfo['success'], $track->success);
 313      }
 314  
 315      /**
 316       * Test exceptions when creating an invalid attempt.
 317       *
 318       * @dataProvider create_attempt_exceptions_data
 319       *
 320       * @param bool $validmod if the activity id is provided
 321       * @param bool $validuser if the user id is provided
 322       */
 323      public function test_create_attempt_exceptions(bool $validmod, bool $validuser) {
 324          global $DB;
 325          $this->resetAfterTest();
 326          $this->setAdminUser();
 327  
 328          $course = $this->getDataGenerator()->create_course();
 329          $activity = $this->getDataGenerator()->create_module('h5pactivity', ['course' => $course]);
 330          $user = $this->getDataGenerator()->create_and_enrol($course, 'student');
 331  
 332          $this->expectException(coding_exception::class);
 333  
 334          $attemptinfo = [
 335              'attempt' => 1,
 336              'interactiontype' => 'compound',
 337              'rawscore' => 2,
 338              'maxscore' => 1,
 339              'duration' => 1,
 340              'completion' => 1,
 341              'success' => 0,
 342          ];
 343  
 344          if ($validmod) {
 345              $attemptinfo['h5pactivityid'] = $activity->id;
 346          }
 347  
 348          if ($validuser) {
 349              $attemptinfo['userid'] = $user->id;
 350          }
 351  
 352          $generator = $this->getDataGenerator()->get_plugin_generator('mod_h5pactivity');
 353          $generator->create_attempt($attemptinfo);
 354      }
 355  
 356      /**
 357       * Data provider for data request creation tests.
 358       *
 359       * @return array
 360       */
 361      public function create_attempt_exceptions_data(): array {
 362          return [
 363              'Invalid user'                  => [true, false],
 364              'Invalid activity'              => [false, true],
 365              'Invalid user and activity'     => [false, false],
 366          ];
 367      }
 368  }