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   * Testing the H5P API.
  19   *
  20   * @package    core_h5p
  21   * @category   test
  22   * @copyright  2020 Sara Arjona <sara@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  declare(strict_types = 1);
  27  
  28  namespace core_h5p;
  29  
  30  defined('MOODLE_INTERNAL') || die();
  31  
  32  /**
  33   * Test class covering the H5P API.
  34   *
  35   * @package    core_h5p
  36   * @copyright  2020 Sara Arjona <sara@moodle.com>
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class api_testcase extends \advanced_testcase {
  40  
  41      /**
  42       * Test the behaviour of delete_library().
  43       *
  44       * @dataProvider  delete_library_provider
  45       * @param  string $libraryname          Machine name of the library to delete.
  46       * @param  int    $expectedh5p          Total of H5P contents expected after deleting the library.
  47       * @param  int    $expectedlibraries    Total of H5P libraries expected after deleting the library.
  48       * @param  int    $expectedcontents     Total of H5P content_libraries expected after deleting the library.
  49       * @param  int    $expecteddependencies Total of H5P library dependencies expected after deleting the library.
  50       */
  51      public function test_delete_library(string $libraryname, int $expectedh5p, int $expectedlibraries,
  52              int $expectedcontents, int $expecteddependencies): void {
  53          global $DB;
  54  
  55          $this->setRunTestInSeparateProcess(true);
  56          $this->resetAfterTest();
  57  
  58          // Generate h5p related data.
  59          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
  60          $generator->generate_h5p_data();
  61          $generator->create_library_record('H5P.TestingLibrary', 'TestingLibrary', 1, 0);
  62  
  63          // Check the current content in H5P tables is the expected.
  64          $counth5p = $DB->count_records('h5p');
  65          $counth5plibraries = $DB->count_records('h5p_libraries');
  66          $counth5pcontents = $DB->count_records('h5p_contents_libraries');
  67          $counth5pdependencies = $DB->count_records('h5p_library_dependencies');
  68  
  69          $this->assertSame(1, $counth5p);
  70          $this->assertSame(7, $counth5plibraries);
  71          $this->assertSame(5, $counth5pcontents);
  72          $this->assertSame(7, $counth5pdependencies);
  73  
  74          // Delete this library.
  75          $factory = new factory();
  76          $library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname]);
  77          if ($library) {
  78              api::delete_library($factory, $library);
  79          }
  80  
  81          // Check the expected libraries and content have been removed.
  82          $counth5p = $DB->count_records('h5p');
  83          $counth5plibraries = $DB->count_records('h5p_libraries');
  84          $counth5pcontents = $DB->count_records('h5p_contents_libraries');
  85          $counth5pdependencies = $DB->count_records('h5p_library_dependencies');
  86  
  87          $this->assertSame($expectedh5p, $counth5p);
  88          $this->assertSame($expectedlibraries, $counth5plibraries);
  89          $this->assertSame($expectedcontents, $counth5pcontents);
  90          $this->assertSame($expecteddependencies, $counth5pdependencies);
  91      }
  92  
  93      /**
  94       * Data provider for test_delete_library().
  95       *
  96       * @return array
  97       */
  98      public function delete_library_provider(): array {
  99          return [
 100              'Delete MainLibrary' => [
 101                  'MainLibrary',
 102                  0,
 103                  6,
 104                  0,
 105                  4,
 106              ],
 107              'Delete Library1' => [
 108                  'Library1',
 109                  0,
 110                  5,
 111                  0,
 112                  1,
 113              ],
 114              'Delete Library2' => [
 115                  'Library2',
 116                  0,
 117                  4,
 118                  0,
 119                  1,
 120              ],
 121              'Delete Library3' => [
 122                  'Library3',
 123                  0,
 124                  4,
 125                  0,
 126                  0,
 127              ],
 128              'Delete Library4' => [
 129                  'Library4',
 130                  0,
 131                  4,
 132                  0,
 133                  1,
 134              ],
 135              'Delete Library5' => [
 136                  'Library5',
 137                  0,
 138                  3,
 139                  0,
 140                  0,
 141              ],
 142              'Delete a library without dependencies' => [
 143                  'H5P.TestingLibrary',
 144                  1,
 145                  6,
 146                  5,
 147                  7,
 148              ],
 149              'Delete unexisting library' => [
 150                  'LibraryX',
 151                  1,
 152                  7,
 153                  5,
 154                  7,
 155              ],
 156          ];
 157      }
 158  
 159      /**
 160       * Test the behaviour of get_dependent_libraries().
 161       *
 162       * @dataProvider  get_dependent_libraries_provider
 163       * @param  string $libraryname     Machine name of the library to delete.
 164       * @param  int    $expectedvalue   Total of H5P required libraries expected.
 165       */
 166      public function test_get_dependent_libraries(string $libraryname, int $expectedvalue): void {
 167          global $DB;
 168  
 169          $this->resetAfterTest();
 170  
 171          // Generate h5p related data.
 172          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 173          $generator->generate_h5p_data();
 174          $generator->create_library_record('H5P.TestingLibrary', 'TestingLibrary', 1, 0);
 175  
 176          // Get required libraries.
 177          $library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname], 'id');
 178          if ($library) {
 179              $libraries = api::get_dependent_libraries((int)$library->id);
 180          } else {
 181              $libraries = [];
 182          }
 183  
 184          $this->assertCount($expectedvalue, $libraries);
 185      }
 186  
 187      /**
 188       * Data provider for test_get_dependent_libraries().
 189       *
 190       * @return array
 191       */
 192      public function get_dependent_libraries_provider(): array {
 193          return [
 194              'Main library of a content' => [
 195                  'MainLibrary',
 196                  0,
 197              ],
 198              'Library1' => [
 199                  'Library1',
 200                  1,
 201              ],
 202              'Library2' => [
 203                  'Library2',
 204                  2,
 205              ],
 206              'Library without dependencies' => [
 207                  'H5P.TestingLibrary',
 208                  0,
 209              ],
 210              'Unexisting library' => [
 211                  'LibraryX',
 212                  0,
 213              ],
 214          ];
 215      }
 216  
 217      /**
 218       * Test the behaviour of get_library().
 219       *
 220       * @dataProvider  get_library_provider
 221       * @param  string $libraryname     Machine name of the library to delete.
 222       * @param  bool   $emptyexpected   Wether the expected result is empty or not.
 223       */
 224      public function test_get_library(string $libraryname, bool $emptyexpected): void {
 225          global $DB;
 226  
 227          $this->resetAfterTest();
 228  
 229          // Generate h5p related data.
 230          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 231          $generator->generate_h5p_data();
 232          $generator->create_library_record('H5P.TestingLibrary', 'TestingLibrary', 1, 0);
 233  
 234          // Get the library identifier.
 235          $library = $DB->get_record('h5p_libraries', ['machinename' => $libraryname], 'id');
 236          if ($library) {
 237              $result = api::get_library((int)$library->id);
 238          } else {
 239              $result = null;
 240          }
 241  
 242          if ($emptyexpected) {
 243              $this->assertEmpty($result);
 244          } else {
 245              $this->assertEquals($library->id, $result->id);
 246              $this->assertEquals($libraryname, $result->machinename);
 247          }
 248  
 249      }
 250  
 251      /**
 252       * Data provider for test_get_library().
 253       *
 254       * @return array
 255       */
 256      public function get_library_provider(): array {
 257          return [
 258              'Main library of a content' => [
 259                  'MainLibrary',
 260                  false,
 261              ],
 262              'Library1' => [
 263                  'Library1',
 264                  false,
 265              ],
 266              'Library without dependencies' => [
 267                  'H5P.TestingLibrary',
 268                  false,
 269              ],
 270              'Unexisting library' => [
 271                  'LibraryX',
 272                  true,
 273              ],
 274          ];
 275      }
 276  
 277      /**
 278       * Test the behaviour of get_content_from_pluginfile_url().
 279       */
 280      public function test_get_content_from_pluginfile_url(): void {
 281          $this->setRunTestInSeparateProcess(true);
 282          $this->resetAfterTest();
 283          $factory = new factory();
 284  
 285          // Create the H5P data.
 286          $filename = 'find-the-words.h5p';
 287          $path = __DIR__ . '/fixtures/' . $filename;
 288          $fakefile = helper::create_fake_stored_file_from_path($path);
 289          $config = (object)[
 290              'frame' => 1,
 291              'export' => 1,
 292              'embed' => 0,
 293              'copyright' => 0,
 294          ];
 295  
 296          // Get URL for this H5P content file.
 297          $syscontext = \context_system::instance();
 298          $url = \moodle_url::make_pluginfile_url(
 299              $syscontext->id,
 300              \core_h5p\file_storage::COMPONENT,
 301              'unittest',
 302              $fakefile->get_itemid(),
 303              '/',
 304              $filename
 305          );
 306  
 307          // Scenario 1: Get the H5P for this URL and check there isn't any existing H5P (because it hasn't been saved).
 308          list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out());
 309          $this->assertEquals($fakefile->get_pathnamehash(), $newfile->get_pathnamehash());
 310          $this->assertEquals($fakefile->get_contenthash(), $newfile->get_contenthash());
 311          $this->assertFalse($h5p);
 312  
 313          // Scenario 2: Save the H5P and check now the H5P is exactly the same as the original one.
 314          $h5pid = helper::save_h5p($factory, $fakefile, $config);
 315          list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out());
 316  
 317          $this->assertEquals($h5pid, $h5p->id);
 318          $this->assertEquals($fakefile->get_pathnamehash(), $h5p->pathnamehash);
 319          $this->assertEquals($fakefile->get_contenthash(), $h5p->contenthash);
 320  
 321          // Scenario 3: Get the H5P for an unexisting H5P file.
 322          $url = \moodle_url::make_pluginfile_url(
 323              $syscontext->id,
 324              \core_h5p\file_storage::COMPONENT,
 325              'unittest',
 326              $fakefile->get_itemid(),
 327              '/',
 328              'unexisting.h5p'
 329          );
 330          list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out());
 331          $this->assertFalse($newfile);
 332          $this->assertFalse($h5p);
 333      }
 334  
 335      /**
 336       * Test the behaviour of create_content_from_pluginfile_url().
 337       */
 338      public function test_create_content_from_pluginfile_url(): void {
 339          global $DB;
 340  
 341          $this->setRunTestInSeparateProcess(true);
 342          $this->resetAfterTest();
 343          $factory = new factory();
 344  
 345          // Create the H5P data.
 346          $filename = 'find-the-words.h5p';
 347          $path = __DIR__ . '/fixtures/' . $filename;
 348          $fakefile = helper::create_fake_stored_file_from_path($path);
 349          $config = (object)[
 350              'frame' => 1,
 351              'export' => 1,
 352              'embed' => 0,
 353              'copyright' => 0,
 354          ];
 355  
 356          // Get URL for this H5P content file.
 357          $syscontext = \context_system::instance();
 358          $url = \moodle_url::make_pluginfile_url(
 359              $syscontext->id,
 360              \core_h5p\file_storage::COMPONENT,
 361              'unittest',
 362              $fakefile->get_itemid(),
 363              '/',
 364              $filename
 365          );
 366  
 367          // Scenario 1: Create the H5P from this URL and check the content is exactly the same as the fake file.
 368          $messages = new \stdClass();
 369          list($newfile, $h5pid) = api::create_content_from_pluginfile_url($url->out(), $config, $factory, $messages);
 370          $this->assertNotFalse($h5pid);
 371          $h5p = $DB->get_record('h5p', ['id' => $h5pid]);
 372          $this->assertEquals($fakefile->get_pathnamehash(), $h5p->pathnamehash);
 373          $this->assertEquals($fakefile->get_contenthash(), $h5p->contenthash);
 374          $this->assertTrue(empty($messages->error));
 375          $this->assertTrue(empty($messages->info));
 376  
 377          // Scenario 2: Create the H5P for an unexisting H5P file.
 378          $url = \moodle_url::make_pluginfile_url(
 379              $syscontext->id,
 380              \core_h5p\file_storage::COMPONENT,
 381              'unittest',
 382              $fakefile->get_itemid(),
 383              '/',
 384              'unexisting.h5p'
 385          );
 386          list($newfile, $h5p) = api::create_content_from_pluginfile_url($url->out(), $config, $factory, $messages);
 387          $this->assertFalse($newfile);
 388          $this->assertFalse($h5p);
 389          $this->assertTrue(empty($messages->error));
 390          $this->assertTrue(empty($messages->info));
 391      }
 392  
 393      /**
 394       * Test the behaviour of delete_content_from_pluginfile_url().
 395       */
 396      public function test_delete_content_from_pluginfile_url(): void {
 397          global $DB;
 398  
 399          $this->setRunTestInSeparateProcess(true);
 400          $this->resetAfterTest();
 401          $factory = new factory();
 402  
 403          // Create the H5P data.
 404          $filename = 'find-the-words.h5p';
 405          $path = __DIR__ . '/fixtures/' . $filename;
 406          $fakefile = helper::create_fake_stored_file_from_path($path);
 407          $config = (object)[
 408              'frame' => 1,
 409              'export' => 1,
 410              'embed' => 0,
 411              'copyright' => 0,
 412          ];
 413  
 414          // Get URL for this H5P content file.
 415          $syscontext = \context_system::instance();
 416          $url = \moodle_url::make_pluginfile_url(
 417              $syscontext->id,
 418              \core_h5p\file_storage::COMPONENT,
 419              'unittest',
 420              $fakefile->get_itemid(),
 421              '/',
 422              $filename
 423          );
 424  
 425          // Scenario 1: Try to remove the H5P content for an undeployed file.
 426          list($newfile, $h5p) = api::get_content_from_pluginfile_url($url->out());
 427          $this->assertEquals(0, $DB->count_records('h5p'));
 428          api::delete_content_from_pluginfile_url($url->out(), $factory);
 429          $this->assertEquals(0, $DB->count_records('h5p'));
 430  
 431          // Scenario 2: Deploy an H5P from this URL, check it's created, remove it and check it has been removed as expected.
 432          $this->assertEquals(0, $DB->count_records('h5p'));
 433  
 434          $messages = new \stdClass();
 435          list($newfile, $h5pid) = api::create_content_from_pluginfile_url($url->out(), $config, $factory, $messages);
 436          $this->assertEquals(1, $DB->count_records('h5p'));
 437  
 438          api::delete_content_from_pluginfile_url($url->out(), $factory);
 439          $this->assertEquals(0, $DB->count_records('h5p'));
 440  
 441          // Scenario 3: Try to remove the H5P for an unexisting H5P URL.
 442          $url = \moodle_url::make_pluginfile_url(
 443              $syscontext->id,
 444              \core_h5p\file_storage::COMPONENT,
 445              'unittest',
 446              $fakefile->get_itemid(),
 447              '/',
 448              'unexisting.h5p'
 449          );
 450          $this->assertEquals(0, $DB->count_records('h5p'));
 451          api::delete_content_from_pluginfile_url($url->out(), $factory);
 452          $this->assertEquals(0, $DB->count_records('h5p'));
 453      }
 454  
 455      /**
 456       * Test the behaviour of get_export_info_from_context_id().
 457       */
 458      public function test_get_export_info_from_context_id(): void {
 459          global $DB;
 460  
 461          $this->setRunTestInSeparateProcess(true);
 462          $this->resetAfterTest();
 463          $factory = new factory();
 464  
 465          // Create the H5P data.
 466          $filename = 'find-the-words.h5p';
 467          $syscontext = \context_system::instance();
 468  
 469          // Test scenario 1: H5P exists and deployed.
 470          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 471          $fakeexportfile = $generator->create_export_file($filename,
 472              $syscontext->id,
 473              \core_h5p\file_storage::COMPONENT,
 474              \core_h5p\file_storage::EXPORT_FILEAREA);
 475  
 476          $exportfile = api::get_export_info_from_context_id($syscontext->id,
 477              $factory,
 478              \core_h5p\file_storage::COMPONENT,
 479              \core_h5p\file_storage::EXPORT_FILEAREA);
 480          $this->assertEquals($fakeexportfile['filename'], $exportfile['filename']);
 481          $this->assertEquals($fakeexportfile['filepath'], $exportfile['filepath']);
 482          $this->assertEquals($fakeexportfile['filesize'], $exportfile['filesize']);
 483          $this->assertEquals($fakeexportfile['timemodified'], $exportfile['timemodified']);
 484          $this->assertEquals($fakeexportfile['fileurl'], $exportfile['fileurl']);
 485  
 486          // Test scenario 2: H5P exist, deployed but the content has changed.
 487          // We need to change the contenthash to simulate the H5P file was changed.
 488          $h5pfile = $DB->get_record('h5p', []);
 489          $h5pfile->contenthash = sha1('testedit');
 490          $DB->update_record('h5p', $h5pfile);
 491          $exportfile = api::get_export_info_from_context_id($syscontext->id,
 492              $factory,
 493              \core_h5p\file_storage::COMPONENT,
 494              \core_h5p\file_storage::EXPORT_FILEAREA);
 495          $this->assertNull($exportfile);
 496  
 497          // Tests scenario 3: H5P is not deployed.
 498          // We need to delete the H5P record to simulate the H5P was not deployed.
 499          $DB->delete_records('h5p', ['id' => $h5pfile->id]);
 500          $exportfile = api::get_export_info_from_context_id($syscontext->id,
 501              $factory,
 502              \core_h5p\file_storage::COMPONENT,
 503              \core_h5p\file_storage::EXPORT_FILEAREA);
 504          $this->assertNull($exportfile);
 505      }
 506  }