Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

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