Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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  namespace core_h5p;
  18  
  19  use core_collator;
  20  use Moodle\H5PCore;
  21  use Moodle\H5PDisplayOptionBehaviour;
  22  
  23  /**
  24   *
  25   * Test class covering the H5PFrameworkInterface interface implementation.
  26   *
  27   * @package    core_h5p
  28   * @category   test
  29   * @copyright  2019 Mihail Geshoski <mihail@moodle.com>
  30   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  31   * @runTestsInSeparateProcesses
  32   */
  33  class framework_test extends \advanced_testcase {
  34  
  35      /** @var \core_h5p\framework */
  36      private $framework;
  37  
  38      /**
  39       * Set up function for tests.
  40       */
  41      public function setUp(): void {
  42          $factory = new \core_h5p\factory();
  43          $this->framework = $factory->get_framework();
  44      }
  45  
  46      /**
  47       * Test the behaviour of getPlatformInfo().
  48       */
  49      public function test_getPlatformInfo() {
  50          global $CFG;
  51  
  52          $platforminfo = $this->framework->getPlatformInfo();
  53  
  54          $expected = array(
  55              'name' => 'Moodle',
  56              'version' => $CFG->version,
  57              'h5pVersion' => $CFG->version
  58          );
  59  
  60          $this->assertEquals($expected, $platforminfo);
  61      }
  62  
  63      /**
  64       * Test the behaviour of fetchExternalData() when the store path is not defined.
  65       *
  66       * This test is intensive and requires downloading content of an external file,
  67       * therefore it might take longer time to execute.
  68       * In order to execute this test PHPUNIT_LONGTEST should be set to true in phpunit.xml or directly in config.php.
  69       */
  70      public function test_fetchExternalData_no_path_defined() {
  71  
  72          if (!PHPUNIT_LONGTEST) {
  73              $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
  74          }
  75  
  76          $this->resetAfterTest();
  77  
  78          $library = 'H5P.Accordion';
  79          // Provide a valid URL to an external H5P content.
  80          $url = $this->getExternalTestFileUrl('/'.$library.'.h5p');
  81  
  82          // Test fetching an external H5P content without defining a path to where the file should be stored.
  83          $data = $this->framework->fetchExternalData($url, null, true);
  84  
  85          // The response should not be empty and return true if the file was successfully downloaded.
  86          $this->assertNotEmpty($data);
  87          $this->assertTrue($data);
  88  
  89          $h5pfolderpath = $this->framework->getUploadedH5pFolderPath();
  90          // The uploaded file should exist on the filesystem.
  91          $this->assertTrue(file_exists($h5pfolderpath . '.h5p'));
  92      }
  93  
  94      /**
  95       * Test the behaviour of fetchExternalData() when the store path is defined.
  96       *
  97       * This test is intensive and requires downloading content of an external file,
  98       * therefore it might take longer time to execute.
  99       * In order to execute this test PHPUNIT_LONGTEST should be set to true in phpunit.xml or directly in config.php.
 100       */
 101      public function test_fetchExternalData_path_defined() {
 102          global $CFG;
 103  
 104          if (!PHPUNIT_LONGTEST) {
 105              $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
 106          }
 107  
 108          $this->resetAfterTest();
 109  
 110          $library = 'H5P.Accordion';
 111          // Provide a valid URL to an external H5P content.
 112          $url = $this->getExternalTestFileUrl('/'.$library.'.h5p');
 113  
 114          $h5pfolderpath = $CFG->tempdir . uniqid('/h5p-');
 115  
 116          $data = $this->framework->fetchExternalData($url, null, true, $h5pfolderpath . '.h5p');
 117  
 118          // The response should not be empty and return true if the content has been successfully saved to a file.
 119          $this->assertNotEmpty($data);
 120          $this->assertTrue($data);
 121  
 122          // The uploaded file should exist on the filesystem.
 123          $this->assertTrue(file_exists($h5pfolderpath . '.h5p'));
 124      }
 125  
 126      /**
 127       * Test the behaviour of fetchExternalData() when the URL is pointing to an external file that is
 128       * not an h5p content.
 129       *
 130       * This test is intensive and requires downloading content of an external file,
 131       * therefore it might take longer time to execute.
 132       * In order to execute this test PHPUNIT_LONGTEST should be set to true in phpunit.xml or directly in config.php.
 133       */
 134      public function test_fetchExternalData_url_not_h5p() {
 135  
 136          if (!PHPUNIT_LONGTEST) {
 137              // This test is intensive and requires downloading the content of an external file.
 138              $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
 139          }
 140  
 141          $this->resetAfterTest();
 142  
 143          // Provide an URL to an external file that is not an H5P content file.
 144          $url = $this->getExternalTestFileUrl('/h5pcontenttypes.json');
 145  
 146          $data = $this->framework->fetchExternalData($url, null, true);
 147  
 148          // The response should not be empty and return true if the content has been successfully saved to a file.
 149          $this->assertNotEmpty($data);
 150          $this->assertTrue($data);
 151  
 152          // The uploaded file should exist on the filesystem with it's original extension.
 153          // NOTE: The file would be later validated by the H5P Validator.
 154          $h5pfolderpath = $this->framework->getUploadedH5pFolderPath();
 155          $this->assertTrue(file_exists($h5pfolderpath . '.json'));
 156      }
 157  
 158      /**
 159       * Test the behaviour of fetchExternalData() when the URL is invalid.
 160       */
 161      public function test_fetchExternalData_url_invalid() {
 162          // Provide an invalid URL to an external file.
 163          $url = "someprotocol://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
 164  
 165          $data = $this->framework->fetchExternalData($url, null, true);
 166  
 167          // The response should be empty.
 168          $this->assertEmpty($data);
 169      }
 170  
 171      /**
 172       * Test the behaviour of setLibraryTutorialUrl().
 173       */
 174      public function test_setLibraryTutorialUrl() {
 175          global $DB;
 176  
 177          $this->resetAfterTest();
 178  
 179          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 180  
 181          // Create several libraries records.
 182          $lib1 = $generator->create_library_record('Library1', 'Lib1', 1, 0, 1, '', null, 'http://tutorial1.org',
 183              'http://example.org');
 184          $lib2 = $generator->create_library_record('Library2', 'Lib2', 2, 0, 1, '', null, 'http://tutorial2.org');
 185          $lib3 = $generator->create_library_record('Library3', 'Lib3', 3, 0);
 186  
 187          // Check only lib1 tutorial URL is updated.
 188          $url = 'https://newtutorial.cat';
 189          $this->framework->setLibraryTutorialUrl($lib1->machinename, $url);
 190  
 191          $libraries = $DB->get_records('h5p_libraries');
 192          $this->assertEquals($libraries[$lib1->id]->tutorial, $url);
 193          $this->assertNotEquals($libraries[$lib2->id]->tutorial, $url);
 194  
 195          // Check lib1 tutorial URL is set to null.
 196          $this->framework->setLibraryTutorialUrl($lib1->machinename, null);
 197  
 198          $libraries = $DB->get_records('h5p_libraries');
 199          $this->assertCount(3, $libraries);
 200          $this->assertNull($libraries[$lib1->id]->tutorial);
 201  
 202          // Check no tutorial URL is set if library name doesn't exist.
 203          $this->framework->setLibraryTutorialUrl('Unexisting library', $url);
 204  
 205          $libraries = $DB->get_records('h5p_libraries');
 206          $this->assertCount(3, $libraries);
 207          $this->assertNull($libraries[$lib1->id]->tutorial);
 208          $this->assertEquals($libraries[$lib2->id]->tutorial, 'http://tutorial2.org');
 209          $this->assertNull($libraries[$lib3->id]->tutorial);
 210  
 211          // Check tutorial is set as expected when it was null.
 212          $this->framework->setLibraryTutorialUrl($lib3->machinename, $url);
 213  
 214          $libraries = $DB->get_records('h5p_libraries');
 215          $this->assertEquals($libraries[$lib3->id]->tutorial, $url);
 216          $this->assertNull($libraries[$lib1->id]->tutorial);
 217          $this->assertEquals($libraries[$lib2->id]->tutorial, 'http://tutorial2.org');
 218      }
 219  
 220      /**
 221       * Test the behaviour of setErrorMessage().
 222       */
 223      public function test_setErrorMessage() {
 224          // Set an error message and an error code.
 225          $message = "Error message";
 226          $code = '404';
 227  
 228          // Set an error message.
 229          $this->framework->setErrorMessage($message, $code);
 230  
 231          // Get the error messages.
 232          $errormessages = $this->framework->getMessages('error');
 233  
 234          $expected = new \stdClass();
 235          $expected->code = 404;
 236          $expected->message = 'Error message';
 237  
 238          $this->assertEquals($expected, $errormessages[0]);
 239      }
 240  
 241      /**
 242       * Test the behaviour of setInfoMessage().
 243       */
 244      public function test_setInfoMessage() {
 245          $message = "Info message";
 246  
 247          // Set an info message.
 248          $this->framework->setInfoMessage($message);
 249  
 250          // Get the info messages.
 251          $infomessages = $this->framework->getMessages('info');
 252  
 253          $expected = 'Info message';
 254  
 255          $this->assertEquals($expected, $infomessages[0]);
 256      }
 257  
 258      /**
 259       * Test the behaviour of getMessages() when requesting the info messages.
 260       */
 261      public function test_getMessages_info() {
 262          // Set an info message.
 263          $this->framework->setInfoMessage("Info message");
 264          // Set an error message.
 265          $this->framework->setErrorMessage("Error message 1", 404);
 266  
 267          // Get the info messages.
 268          $infomessages = $this->framework->getMessages('info');
 269  
 270          $expected = 'Info message';
 271  
 272          // Make sure that only the info message has been returned.
 273          $this->assertCount(1, $infomessages);
 274          $this->assertEquals($expected, $infomessages[0]);
 275  
 276          $infomessages = $this->framework->getMessages('info');
 277  
 278          // Make sure the info messages have now been removed.
 279          $this->assertEmpty($infomessages);
 280      }
 281  
 282      /**
 283       * Test the behaviour of getMessages() when requesting the error messages.
 284       */
 285      public function test_getMessages_error() {
 286          // Set an info message.
 287          $this->framework->setInfoMessage("Info message");
 288          // Set an error message.
 289          $this->framework->setErrorMessage("Error message 1", 404);
 290          // Set another error message.
 291          $this->framework->setErrorMessage("Error message 2", 403);
 292  
 293          // Get the error messages.
 294          $errormessages = $this->framework->getMessages('error');
 295  
 296          // Make sure that only the error messages are being returned.
 297          $this->assertEquals(2, count($errormessages));
 298  
 299          $expected1 = (object) [
 300              'code' => 404,
 301              'message' => 'Error message 1'
 302          ];
 303  
 304          $expected2 = (object) [
 305              'code' => 403,
 306              'message' => 'Error message 2'
 307          ];
 308  
 309          $this->assertEquals($expected1, $errormessages[0]);
 310          $this->assertEquals($expected2, $errormessages[1]);
 311  
 312          $errormessages = $this->framework->getMessages('error');
 313  
 314          // Make sure the info messages have now been removed.
 315          $this->assertEmpty($errormessages);
 316      }
 317  
 318      /**
 319       * Test the behaviour of t() when translating existing string that does not require any arguments.
 320       */
 321      public function test_t_existing_string_no_args() {
 322          // Existing language string without passed arguments.
 323          $translation = $this->framework->t('No copyright information available for this content.');
 324  
 325          // Make sure the string translation has been returned.
 326          $this->assertEquals('No copyright information available for this content.', $translation);
 327      }
 328  
 329      /**
 330       * Test the behaviour of t() when translating existing string that does require parameters.
 331       */
 332      public function test_t_existing_string_args() {
 333          // Existing language string with passed arguments.
 334          $translation = $this->framework->t('Illegal option %option in %library',
 335              ['%option' => 'example', '%library' => 'Test library']);
 336  
 337          // Make sure the string translation has been returned.
 338          $this->assertEquals('Illegal option example in Test library', $translation);
 339      }
 340  
 341      /**
 342       * Test the behaviour of t() when translating non-existent string.
 343       */
 344      public function test_t_non_existent_string() {
 345          // Non-existing language string.
 346          $message = 'Random message %option';
 347  
 348          $translation = $this->framework->t($message);
 349  
 350          // Make sure a debugging message is triggered.
 351          $this->assertDebuggingCalled("String translation cannot be found. Please add a string definition for '" .
 352              $message . "' in the core_h5p component.");
 353          // As the string does not exist in the mapping array, make sure the passed message is returned.
 354          $this->assertEquals($message, $translation);
 355      }
 356  
 357      /**
 358       * Test the behaviour of getLibraryFileUrl() when requesting a file URL from an existing library and
 359       * the folder name is parsable.
 360       **/
 361      public function test_getLibraryFileUrl() {
 362          global $CFG;
 363  
 364          $this->resetAfterTest();
 365  
 366          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 367          // Create a library record.
 368          $lib = $generator->create_library_record('Library', 'Lib', 1, 1);
 369  
 370          $expected = "{$CFG->wwwroot}/pluginfile.php/1/core_h5p/libraries/{$lib->id}/Library-1.1/library.json";
 371  
 372          // Get the URL of a file from an existing library. The provided folder name is parsable.
 373          $actual = $this->framework->getLibraryFileUrl('Library-1.1', 'library.json');
 374  
 375          // Make sure the expected URL is returned.
 376          $this->assertEquals($expected, $actual);
 377      }
 378  
 379      /**
 380       * Test the behaviour of getLibraryFileUrl() when requesting a file URL from a non-existent library and
 381       * the folder name is parsable.
 382       **/
 383      public function test_getLibraryFileUrl_non_existent_library() {
 384          $this->resetAfterTest();
 385  
 386          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 387          // Create a library record.
 388          $generator->create_library_record('Library', 'Lib', 1, 1);
 389  
 390          // Get the URL of a file from a non-existent library. The provided folder name is parsable.
 391          $actual = $this->framework->getLibraryFileUrl('Library2-1.1', 'library.json');
 392  
 393          // Make sure a debugging message is triggered.
 394          $this->assertDebuggingCalled('The library "Library2-1.1" does not exist.');
 395  
 396          // Make sure that an URL is not returned.
 397          $this->assertEquals(null, $actual);
 398      }
 399  
 400      /**
 401       * Test the behaviour of getLibraryFileUrl() when requesting a file URL from an existing library and
 402       * the folder name is not parsable.
 403       **/
 404      public function test_getLibraryFileUrl_not_parsable_folder_name() {
 405          $this->resetAfterTest();
 406  
 407          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 408          // Create a library record.
 409          $generator->create_library_record('Library', 'Lib', 1, 1);
 410  
 411          // Get the URL of a file from an existing library. The provided folder name is not parsable.
 412          $actual = $this->framework->getLibraryFileUrl('Library1.1', 'library.json');
 413  
 414          // Make sure a debugging message is triggered.
 415          $this->assertDebuggingCalled(
 416              'The provided string value "Library1.1" is not a valid name for a library folder.');
 417  
 418          // Make sure that an URL is not returned.
 419          $this->assertEquals(null, $actual);
 420      }
 421  
 422      /**
 423       * Test the behaviour of getLibraryFileUrl() when requesting a file URL from a library that has multiple
 424       * versions and the folder name is parsable.
 425       **/
 426      public function test_getLibraryFileUrl_library_has_multiple_versions() {
 427          global $CFG;
 428  
 429          $this->resetAfterTest();
 430  
 431          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 432          // Create library records with a different minor version.
 433          $lib1 = $generator->create_library_record('Library', 'Lib', 1, 1);
 434          $lib2 = $generator->create_library_record('Library', 'Lib', 1, 3);
 435  
 436          $expected = "{$CFG->wwwroot}/pluginfile.php/1/core_h5p/libraries/{$lib2->id}/Library-1.3/library.json";
 437  
 438          // Get the URL of a file from an existing library (Library 1.3). The provided folder name is parsable.
 439          $actual = $this->framework->getLibraryFileUrl('Library-1.3', 'library.json');
 440  
 441          // Make sure the proper URL (from the requested library version) is returned.
 442          $this->assertEquals($expected, $actual);
 443      }
 444  
 445      /**
 446       * Test the behaviour of getLibraryFileUrl() when requesting a file URL from a library that has multiple
 447       * patch versions and the folder name is parsable.
 448       **/
 449      public function test_getLibraryFileUrl_library_has_multiple_patch_versions() {
 450          global $CFG;
 451  
 452          $this->resetAfterTest();
 453  
 454          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 455          // Create library records with a different patch version.
 456          $lib1 = $generator->create_library_record('Library', 'Lib', 1, 1, 2);
 457          $lib2 = $generator->create_library_record('Library', 'Lib', 1, 1, 4);
 458          $lib3 = $generator->create_library_record('Library', 'Lib', 1, 1, 3);
 459  
 460          $expected = "{$CFG->wwwroot}/pluginfile.php/1/core_h5p/libraries/{$lib2->id}/Library-1.1/library.json";
 461  
 462          // Get the URL of a file from an existing library. The provided folder name is parsable.
 463          $actual = $this->framework->getLibraryFileUrl('Library-1.1', 'library.json');
 464  
 465          // Make sure the proper URL (from the latest library patch) is returned.
 466          $this->assertEquals($expected, $actual);
 467      }
 468  
 469      /**
 470       * Test the behaviour of getLibraryFileUrl() when requesting a file URL from a sub-folder
 471       * of an existing library and the folder name is parsable.
 472       **/
 473      public function test_getLibraryFileUrl_library_subfolder() {
 474          global $CFG;
 475  
 476          $this->resetAfterTest();
 477  
 478          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 479          // Create a library record.
 480          $lib = $generator->create_library_record('Library', 'Lib', 1, 1);
 481  
 482          $expected = "{$CFG->wwwroot}/pluginfile.php/1/core_h5p/libraries/{$lib->id}/Library-1.1/css/example.css";
 483  
 484          // Get the URL of a file from a sub-folder from an existing library. The provided folder name is parsable.
 485          $actual = $this->framework->getLibraryFileUrl('Library-1.1/css', 'example.css');
 486  
 487          // Make sure the proper URL is returned.
 488          $this->assertEquals($expected, $actual);
 489      }
 490  
 491      /**
 492       * Test the behaviour of loadAddons().
 493       */
 494      public function test_loadAddons() {
 495          $this->resetAfterTest();
 496  
 497          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 498  
 499          // Create a Library addon (1.1).
 500          $generator->create_library_record('Library', 'Lib', 1, 1, 2,
 501              '', '/regex1/');
 502          // Create a Library addon (1.3).
 503          $generator->create_library_record('Library', 'Lib', 1, 3, 2,
 504              '', '/regex2/');
 505          // Create a Library addon (1.2).
 506          $generator->create_library_record('Library', 'Lib', 1, 2, 2,
 507              '', '/regex3/');
 508          // Create a Library1 addon (1.2)
 509          $generator->create_library_record('Library1', 'Lib1', 1, 2, 2,
 510              '', '/regex11/');
 511  
 512          // Load the latest version of each addon.
 513          $addons = $this->framework->loadAddons();
 514  
 515          // The addons array should return 2 results (Library and Library1 addon).
 516          $this->assertCount(2, $addons);
 517  
 518          // Ensure the addons array is consistently ordered before asserting their contents.
 519          core_collator::asort_array_of_arrays_by_key($addons, 'machineName');
 520          [$addonone, $addontwo] = array_values($addons);
 521  
 522          // Make sure the version 1.3 is the latest 'Library' addon version.
 523          $this->assertEquals('Library', $addonone['machineName']);
 524          $this->assertEquals(1, $addonone['majorVersion']);
 525          $this->assertEquals(3, $addonone['minorVersion']);
 526  
 527          // Make sure the version 1.2 is the latest 'Library1' addon version.
 528          $this->assertEquals('Library1', $addontwo['machineName']);
 529          $this->assertEquals(1, $addontwo['majorVersion']);
 530          $this->assertEquals(2, $addontwo['minorVersion']);
 531      }
 532  
 533      /**
 534       * Test the behaviour of loadLibraries().
 535       */
 536      public function test_loadLibraries() {
 537          $this->resetAfterTest();
 538  
 539          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 540  
 541          // Generate h5p related data.
 542          $generator->generate_h5p_data();
 543  
 544          // Load all libraries.
 545          $libraries = $this->framework->loadLibraries();
 546  
 547          // Make sure all libraries are returned.
 548          $this->assertNotEmpty($libraries);
 549          $this->assertCount(6, $libraries);
 550          $this->assertEquals('MainLibrary', $libraries['MainLibrary'][0]->machine_name);
 551          $this->assertEquals('1', $libraries['MainLibrary'][0]->major_version);
 552          $this->assertEquals('0', $libraries['MainLibrary'][0]->minor_version);
 553          $this->assertEquals('1', $libraries['MainLibrary'][0]->patch_version);
 554      }
 555  
 556      /**
 557       * Test the behaviour of test_getLibraryId() when requesting an existing machine name.
 558       */
 559      public function test_getLibraryId_existing_machine_name() {
 560          $this->resetAfterTest();
 561  
 562          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 563  
 564          // Create a library.
 565          $lib = $generator->create_library_record('Library', 'Lib', 1, 1, 2);
 566  
 567          // Request the library ID of the library with machine name 'Library'.
 568          $libraryid = $this->framework->getLibraryId('Library');
 569  
 570          // Make sure the library ID is being returned.
 571          $this->assertNotFalse($libraryid);
 572          $this->assertEquals($lib->id, $libraryid);
 573      }
 574  
 575      /**
 576       * Test the behaviour of test_getLibraryId() when requesting a non-existent machine name.
 577       */
 578      public function test_getLibraryId_non_existent_machine_name() {
 579          $this->resetAfterTest();
 580  
 581          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 582  
 583          // Create a library.
 584          $generator->create_library_record('Library', 'Lib', 1, 1, 2);
 585  
 586          // Request the library ID of the library with machinename => 'TestLibrary' (non-existent).
 587          $libraryid = $this->framework->getLibraryId('TestLibrary');
 588  
 589          // Make sure the library ID not being returned.
 590          $this->assertFalse($libraryid);
 591      }
 592  
 593      /**
 594       * Test the behaviour of test_getLibraryId() when requesting a non-existent major version.
 595       */
 596      public function test_getLibraryId_non_existent_major_version() {
 597          $this->resetAfterTest();
 598  
 599          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 600  
 601          // Create a library.
 602          $generator->create_library_record('Library', 'Lib', 1, 1, 2);
 603  
 604          // Request the library ID of the library with machine name => 'Library', majorversion => 2 (non-existent).
 605          $libraryid = $this->framework->getLibraryId('Library', 2);
 606  
 607          // Make sure the library ID not being returned.
 608          $this->assertFalse($libraryid);
 609      }
 610  
 611      /**
 612       * Test the behaviour of test_getLibraryId() when requesting a non-existent minor version.
 613       */
 614      public function test_getLibraryId_non_existent_minor_version() {
 615          $this->resetAfterTest();
 616  
 617          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 618  
 619          // Create a library.
 620          $generator->create_library_record('Library', 'Lib', 1, 1, 2);
 621  
 622          // Request the library ID of the library with machine name => 'Library',
 623          // majorversion => 1,  minorversion => 2 (non-existent).
 624          $libraryid = $this->framework->getLibraryId('Library', 1, 2);
 625  
 626          // Make sure the library ID not being returned.
 627          $this->assertFalse($libraryid);
 628      }
 629  
 630      /**
 631       * Test the behaviour of isPatchedLibrary().
 632       *
 633       * @dataProvider isPatchedLibrary_provider
 634       * @param array $libraryrecords Array containing data for the library creation
 635       * @param array $testlibrary Array containing the test library data
 636       * @param bool $expected The expectation whether the library is patched or not
 637       **/
 638      public function test_isPatchedLibrary(array $libraryrecords, array $testlibrary, bool $expected): void {
 639          $this->resetAfterTest();
 640  
 641          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 642  
 643          foreach ($libraryrecords as $library) {
 644              call_user_func_array([$generator, 'create_library_record'], $library);
 645          }
 646  
 647          $this->assertEquals($expected, $this->framework->isPatchedLibrary($testlibrary));
 648      }
 649  
 650      /**
 651       * Data provider for test_isPatchedLibrary().
 652       *
 653       * @return array
 654       */
 655      public function isPatchedLibrary_provider(): array {
 656          return [
 657              'Unpatched library. No different versioning' => [
 658                  [
 659                      ['TestLibrary', 'Test', 1, 1, 2],
 660                  ],
 661                  [
 662                      'machineName' => 'TestLibrary',
 663                      'majorVersion' => 1,
 664                      'minorVersion' => 1,
 665                      'patchVersion' => 2
 666                  ],
 667                  false,
 668              ],
 669              'Major version identical; Minor version identical; Patch version newer' => [
 670                  [
 671                      ['TestLibrary', 'Test', 1, 1, 2],
 672                  ],
 673                  [
 674                      'machineName' => 'TestLibrary',
 675                      'majorVersion' => 1,
 676                      'minorVersion' => 1,
 677                      'patchVersion' => 3
 678                  ],
 679                  true,
 680              ],
 681              'Major version identical; Minor version newer; Patch version newer' => [
 682                  [
 683                      ['TestLibrary', 'Test', 1, 1, 2],
 684                  ],
 685                  [
 686                      'machineName' => 'TestLibrary',
 687                      'majorVersion' => 1,
 688                      'minorVersion' => 2,
 689                      'patchVersion' => 3
 690                  ],
 691                  false,
 692              ],
 693              'Major version identical; Minor version identical; Patch version older' => [
 694                  [
 695                      ['TestLibrary', 'Test', 1, 1, 2],
 696                  ],
 697                  [
 698                      'machineName' => 'TestLibrary',
 699                      'majorVersion' => 1,
 700                      'minorVersion' => 1,
 701                      'patchVersion' => 1
 702                  ],
 703                  false,
 704              ],
 705              'Major version identical; Minor version newer; Patch version older' => [
 706                  [
 707                      ['TestLibrary', 'Test', 1, 1, 2],
 708                  ],
 709                  [
 710                      'machineName' => 'TestLibrary',
 711                      'majorVersion' => 1,
 712                      'minorVersion' => 2,
 713                      'patchVersion' => 1
 714                  ],
 715                  false,
 716              ],
 717              'Major version newer; Minor version identical; Patch version older' => [
 718                  [
 719                      ['TestLibrary', 'Test', 1, 1, 2],
 720                  ],
 721                  [
 722                      'machineName' => 'TestLibrary',
 723                      'majorVersion' => 2,
 724                      'minorVersion' => 1,
 725                      'patchVersion' => 1
 726                  ],
 727                  false,
 728              ],
 729              'Major version newer; Minor version identical; Patch version newer' => [
 730                  [
 731                      ['TestLibrary', 'Test', 1, 1, 2],
 732                  ],
 733                  [
 734                      'machineName' => 'TestLibrary',
 735                      'majorVersion' => 2,
 736                      'minorVersion' => 1,
 737                      'patchVersion' => 3
 738                  ],
 739                  false,
 740              ],
 741  
 742              'Major version older; Minor version identical; Patch version older' => [
 743                  [
 744                      ['TestLibrary', 'Test', 1, 1, 2],
 745                  ],
 746                  [
 747                      'machineName' => 'TestLibrary',
 748                      'majorVersion' => 0,
 749                      'minorVersion' => 1,
 750                      'patchVersion' => 1
 751                  ],
 752                  false,
 753              ],
 754              'Major version older; Minor version identical; Patch version newer' => [
 755                  [
 756                      ['TestLibrary', 'Test', 1, 1, 2],
 757                  ],
 758                  [
 759                      'machineName' => 'TestLibrary',
 760                      'majorVersion' => 0,
 761                      'minorVersion' => 1,
 762                      'patchVersion' => 3
 763                  ],
 764                  false,
 765              ],
 766          ];
 767      }
 768  
 769      /**
 770       * Test the behaviour of isInDevMode().
 771       */
 772      public function test_isInDevMode() {
 773          $isdevmode = $this->framework->isInDevMode();
 774  
 775          $this->assertFalse($isdevmode);
 776      }
 777  
 778      /**
 779       * Test the behaviour of mayUpdateLibraries().
 780       */
 781      public function test_mayUpdateLibraries(): void {
 782          global $DB;
 783  
 784          $this->resetAfterTest();
 785  
 786          // Create some users.
 787          $contextsys = \context_system::instance();
 788          $user = $this->getDataGenerator()->create_user();
 789          $admin = get_admin();
 790          $managerrole = $DB->get_record('role', ['shortname' => 'manager'], '*', MUST_EXIST);
 791          $studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
 792          $manager = $this->getDataGenerator()->create_user();
 793          role_assign($managerrole->id, $manager->id, $contextsys);
 794  
 795          // Create a course with a label and enrol the user.
 796          $course = $this->getDataGenerator()->create_course();
 797          $label = $this->getDataGenerator()->create_module('label', ['course' => $course->id]);
 798          list(, $labelcm) = get_course_and_cm_from_instance($label->id, 'label');
 799          $contextlabel = \context_module::instance($labelcm->id);
 800          $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
 801  
 802          // Create the .h5p file.
 803          $path = __DIR__ . '/fixtures/h5ptest.zip';
 804  
 805          // Admin and manager should have permission to update libraries.
 806          $file = helper::create_fake_stored_file_from_path($path, $admin->id, $contextsys);
 807          $this->framework->set_file($file);
 808          $mayupdatelib = $this->framework->mayUpdateLibraries();
 809          $this->assertTrue($mayupdatelib);
 810  
 811          $file = helper::create_fake_stored_file_from_path($path, $manager->id, $contextsys);
 812          $this->framework->set_file($file);
 813          $mayupdatelib = $this->framework->mayUpdateLibraries();
 814          $this->assertTrue($mayupdatelib);
 815  
 816          // By default, normal user hasn't permission to update libraries (in both contexts, system and module label).
 817          $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextsys);
 818          $this->framework->set_file($file);
 819          $mayupdatelib = $this->framework->mayUpdateLibraries();
 820          $this->assertFalse($mayupdatelib);
 821  
 822          $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextlabel);
 823          $this->framework->set_file($file);
 824          $mayupdatelib = $this->framework->mayUpdateLibraries();
 825          $this->assertFalse($mayupdatelib);
 826  
 827          // If the current user (admin) can update libraries, the method should return true (even if the file userid hasn't the
 828          // required capabilility in the file context).
 829          $file = helper::create_fake_stored_file_from_path($path, $admin->id, $contextlabel);
 830          $this->framework->set_file($file);
 831          $mayupdatelib = $this->framework->mayUpdateLibraries();
 832          $this->assertTrue($mayupdatelib);
 833  
 834          // If the update capability is assigned to the user, they should be able to update the libraries (only in the context
 835          // where the capability has been assigned).
 836          $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextlabel);
 837          $this->framework->set_file($file);
 838          $mayupdatelib = $this->framework->mayUpdateLibraries();
 839          $this->assertFalse($mayupdatelib);
 840          assign_capability('moodle/h5p:updatelibraries', CAP_ALLOW, $studentrole->id, $contextlabel);
 841          $mayupdatelib = $this->framework->mayUpdateLibraries();
 842          $this->assertTrue($mayupdatelib);
 843          $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextsys);
 844          $this->framework->set_file($file);
 845          $mayupdatelib = $this->framework->mayUpdateLibraries();
 846          $this->assertFalse($mayupdatelib);
 847      }
 848  
 849      /**
 850       * Test the behaviour of get_file() and set_file().
 851       */
 852      public function test_get_file(): void {
 853          $this->resetAfterTest();
 854  
 855          // Create some users.
 856          $contextsys = \context_system::instance();
 857          $user = $this->getDataGenerator()->create_user();
 858  
 859          // The H5P file.
 860          $path = __DIR__ . '/fixtures/h5ptest.zip';
 861  
 862          // An error should be raised when it's called before initialitzing it.
 863          $this->expectException('coding_exception');
 864          $this->expectExceptionMessage('Using get_file() before file is set');
 865          $this->framework->get_file();
 866  
 867          // Check the value when only path and user are set.
 868          $file = helper::create_fake_stored_file_from_path($path, $user->id);
 869          $this->framework->set_file($file);
 870          $file = $this->framework->get_file();
 871          $this->assertEquals($user->id, $$file->get_userid());
 872          $this->assertEquals($contextsys->id, $file->get_contextid());
 873  
 874          // Check the value when also the context is set.
 875          $course = $this->getDataGenerator()->create_course();
 876          $contextcourse = \context_course::instance($course->id);
 877          $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextcourse);
 878          $this->framework->set_file($file);
 879          $file = $this->framework->get_file();
 880          $this->assertEquals($user->id, $$file->get_userid());
 881          $this->assertEquals($contextcourse->id, $file->get_contextid());
 882      }
 883  
 884      /**
 885       * Test the behaviour of saveLibraryData() when saving data for a new library.
 886       */
 887      public function test_saveLibraryData_new_library() {
 888          global $DB;
 889  
 890          $this->resetAfterTest();
 891  
 892          $librarydata = array(
 893              'title' => 'Test',
 894              'machineName' => 'TestLibrary',
 895              'majorVersion' => '1',
 896              'minorVersion' => '0',
 897              'patchVersion' => '2',
 898              'runnable' => 1,
 899              'fullscreen' => 1,
 900              'preloadedJs' => array(
 901                  array(
 902                      'path' => 'js/name.min.js'
 903                  )
 904              ),
 905              'preloadedCss' => array(
 906                  array(
 907                      'path' => 'css/name.css'
 908                  )
 909              ),
 910              'dropLibraryCss' => array(
 911                  array(
 912                      'machineName' => 'Name2'
 913                  )
 914              )
 915          );
 916  
 917          // Create a new library.
 918          $this->framework->saveLibraryData($librarydata);
 919  
 920          $library = $DB->get_record('h5p_libraries', ['machinename' => $librarydata['machineName']]);
 921  
 922          // Make sure the library data was properly saved.
 923          $this->assertNotEmpty($library);
 924          $this->assertNotEmpty($librarydata['libraryId']);
 925          $this->assertEquals($librarydata['title'], $library->title);
 926          $this->assertEquals($librarydata['machineName'], $library->machinename);
 927          $this->assertEquals($librarydata['majorVersion'], $library->majorversion);
 928          $this->assertEquals($librarydata['minorVersion'], $library->minorversion);
 929          $this->assertEquals($librarydata['patchVersion'], $library->patchversion);
 930          $this->assertEquals($librarydata['preloadedJs'][0]['path'], $library->preloadedjs);
 931          $this->assertEquals($librarydata['preloadedCss'][0]['path'], $library->preloadedcss);
 932          $this->assertEquals($librarydata['dropLibraryCss'][0]['machineName'], $library->droplibrarycss);
 933      }
 934  
 935      /**
 936       * Test the behaviour of saveLibraryData() when saving (updating) data for an existing library.
 937       */
 938      public function test_saveLibraryData_existing_library() {
 939          global $DB;
 940  
 941          $this->resetAfterTest();
 942  
 943          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
 944  
 945          // Create a library record.
 946          $library = $generator->create_library_record('TestLibrary', 'Test', 1, 0, 2);
 947  
 948          $librarydata = array(
 949              'libraryId' => $library->id,
 950              'title' => 'Test1',
 951              'machineName' => 'TestLibrary',
 952              'majorVersion' => '1',
 953              'minorVersion' => '2',
 954              'patchVersion' => '2',
 955              'runnable' => 1,
 956              'fullscreen' => 1,
 957              'preloadedJs' => array(
 958                  array(
 959                      'path' => 'js/name.min.js'
 960                  )
 961              ),
 962              'preloadedCss' => array(
 963                  array(
 964                      'path' => 'css/name.css'
 965                  )
 966              ),
 967              'dropLibraryCss' => array(
 968                  array(
 969                      'machineName' => 'Name2'
 970                  )
 971              )
 972          );
 973  
 974          // Update the library.
 975          $this->framework->saveLibraryData($librarydata, false);
 976  
 977          $library = $DB->get_record('h5p_libraries', ['machinename' => $librarydata['machineName']]);
 978  
 979          // Make sure the library data was properly updated.
 980          $this->assertNotEmpty($library);
 981          $this->assertNotEmpty($librarydata['libraryId']);
 982          $this->assertEquals($librarydata['title'], $library->title);
 983          $this->assertEquals($librarydata['machineName'], $library->machinename);
 984          $this->assertEquals($librarydata['majorVersion'], $library->majorversion);
 985          $this->assertEquals($librarydata['minorVersion'], $library->minorversion);
 986          $this->assertEquals($librarydata['patchVersion'], $library->patchversion);
 987          $this->assertEquals($librarydata['preloadedJs'][0]['path'], $library->preloadedjs);
 988          $this->assertEquals($librarydata['preloadedCss'][0]['path'], $library->preloadedcss);
 989          $this->assertEquals($librarydata['dropLibraryCss'][0]['machineName'], $library->droplibrarycss);
 990      }
 991  
 992      /**
 993       * Test the behaviour of insertContent().
 994       */
 995      public function test_insertContent() {
 996          global $DB;
 997  
 998          $this->resetAfterTest();
 999  
1000          $content = array(
1001              'params' => json_encode(['param1' => 'Test']),
1002              'library' => array(
1003                  'libraryId' => 1
1004              ),
1005              'disable' => 8
1006          );
1007  
1008          // Insert h5p content.
1009          $contentid = $this->framework->insertContent($content);
1010  
1011          // Get the entered content from the db.
1012          $dbcontent = $DB->get_record('h5p', ['id' => $contentid]);
1013  
1014          // Make sure the h5p content was properly inserted.
1015          $this->assertNotEmpty($dbcontent);
1016          $this->assertEquals($content['params'], $dbcontent->jsoncontent);
1017          $this->assertEquals($content['library']['libraryId'], $dbcontent->mainlibraryid);
1018          $this->assertEquals($content['disable'], $dbcontent->displayoptions);
1019      }
1020  
1021      /**
1022       * Test the behaviour of insertContent().
1023       */
1024      public function test_insertContent_latestlibrary() {
1025          global $DB;
1026  
1027          $this->resetAfterTest();
1028  
1029          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1030          // Create a library record.
1031          $lib = $generator->create_library_record('TestLibrary', 'Test', 1, 1, 2);
1032  
1033          $content = array(
1034              'params' => json_encode(['param1' => 'Test']),
1035              'library' => array(
1036                  'libraryId' => 0,
1037                  'machineName' => 'TestLibrary',
1038              ),
1039              'disable' => 8
1040          );
1041  
1042          // Insert h5p content.
1043          $contentid = $this->framework->insertContent($content);
1044  
1045          // Get the entered content from the db.
1046          $dbcontent = $DB->get_record('h5p', ['id' => $contentid]);
1047  
1048          // Make sure the h5p content was properly inserted.
1049          $this->assertNotEmpty($dbcontent);
1050          $this->assertEquals($content['params'], $dbcontent->jsoncontent);
1051          $this->assertEquals($content['disable'], $dbcontent->displayoptions);
1052          // As the libraryId was empty, the latest library has been used.
1053          $this->assertEquals($lib->id, $dbcontent->mainlibraryid);
1054      }
1055  
1056      /**
1057       * Test the behaviour of updateContent().
1058       */
1059      public function test_updateContent() {
1060          global $DB;
1061  
1062          $this->resetAfterTest();
1063  
1064          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1065  
1066          // Create a library record.
1067          $lib = $generator->create_library_record('TestLibrary', 'Test', 1, 1, 2);
1068  
1069          // Create an h5p content with 'TestLibrary' as it's main library.
1070          $contentid = $generator->create_h5p_record($lib->id);
1071  
1072          $content = array(
1073              'id' => $contentid,
1074              'params' => json_encode(['param2' => 'Test2']),
1075              'library' => array(
1076                  'libraryId' => $lib->id
1077              ),
1078              'disable' => 8
1079          );
1080  
1081          // Update the h5p content.
1082          $this->framework->updateContent($content);
1083  
1084          $h5pcontent = $DB->get_record('h5p', ['id' => $contentid]);
1085  
1086          // Make sure the h5p content was properly updated.
1087          $this->assertNotEmpty($h5pcontent);
1088          $this->assertEquals($content['params'], $h5pcontent->jsoncontent);
1089          $this->assertEquals($content['library']['libraryId'], $h5pcontent->mainlibraryid);
1090          $this->assertEquals($content['disable'], $h5pcontent->displayoptions);
1091      }
1092  
1093      /**
1094       * Test the behaviour of saveLibraryDependencies().
1095       */
1096      public function test_saveLibraryDependencies() {
1097          global $DB;
1098  
1099          $this->resetAfterTest();
1100  
1101          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1102  
1103          // Create a library 'Library'.
1104          $library = $generator->create_library_record('Library', 'Title');
1105          // Create a library 'DependencyLibrary1'.
1106          $dependency1 = $generator->create_library_record('DependencyLibrary1', 'DependencyTitle1');
1107          // Create a library 'DependencyLibrary2'.
1108          $dependency2 = $generator->create_library_record('DependencyLibrary2', 'DependencyTitle2');
1109  
1110          $dependencies = array(
1111              array(
1112                  'machineName' => $dependency1->machinename,
1113                  'majorVersion' => $dependency1->majorversion,
1114                  'minorVersion' => $dependency1->minorversion
1115              ),
1116              array(
1117                  'machineName' => $dependency2->machinename,
1118                  'majorVersion' => $dependency2->majorversion,
1119                  'minorVersion' => $dependency2->minorversion
1120              ),
1121          );
1122  
1123          // Set 'DependencyLibrary1' and 'DependencyLibrary2' as library dependencies of 'Library'.
1124          $this->framework->saveLibraryDependencies($library->id, $dependencies, 'preloaded');
1125  
1126          $libdependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library->id], 'id ASC');
1127  
1128          // Make sure the library dependencies for 'Library' are properly set.
1129          $this->assertEquals(2, count($libdependencies));
1130          $this->assertEquals($dependency1->id, reset($libdependencies)->requiredlibraryid);
1131          $this->assertEquals($dependency2->id, end($libdependencies)->requiredlibraryid);
1132      }
1133  
1134      /**
1135       * Test the behaviour of deleteContentData().
1136       */
1137      public function test_deleteContentData() {
1138          global $DB;
1139  
1140          $this->resetAfterTest();
1141  
1142          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1143  
1144          // Generate some h5p related data.
1145          $data = $generator->generate_h5p_data();
1146          $h5pid = $data->h5pcontent->h5pid;
1147  
1148          $h5pcontent = $DB->get_record('h5p', ['id' => $h5pid]);
1149          // Make sure the particular h5p content exists in the DB.
1150          $this->assertNotEmpty($h5pcontent);
1151  
1152          // Get the h5p content libraries from the DB.
1153          $h5pcontentlibraries = $DB->get_records('h5p_contents_libraries', ['h5pid' => $h5pid]);
1154  
1155          // Make sure the content libraries exists in the DB.
1156          $this->assertNotEmpty($h5pcontentlibraries);
1157          $this->assertCount(5, $h5pcontentlibraries);
1158  
1159          // Delete the h5p content and it's related data.
1160          $this->framework->deleteContentData($h5pid);
1161  
1162          $h5pcontent = $DB->get_record('h5p', ['id' => $h5pid]);
1163          $h5pcontentlibraries = $DB->get_record('h5p_contents_libraries', ['h5pid' => $h5pid]);
1164  
1165          // The particular h5p content should no longer exist in the db.
1166          $this->assertEmpty($h5pcontent);
1167          // The particular content libraries should no longer exist in the db.
1168          $this->assertEmpty($h5pcontentlibraries);
1169      }
1170  
1171      /**
1172       * Test the behaviour of deleteLibraryUsage().
1173       */
1174      public function test_deleteLibraryUsage() {
1175          global $DB;
1176  
1177          $this->resetAfterTest();
1178  
1179          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1180  
1181          // Generate some h5p related data.
1182          $data = $generator->generate_h5p_data();
1183          $h5pid = $data->h5pcontent->h5pid;
1184  
1185          // Get the h5p content libraries from the DB.
1186          $h5pcontentlibraries = $DB->get_records('h5p_contents_libraries', ['h5pid' => $h5pid]);
1187  
1188          // The particular h5p content should have 5 content libraries.
1189          $this->assertNotEmpty($h5pcontentlibraries);
1190          $this->assertCount(5, $h5pcontentlibraries);
1191  
1192          // Delete the h5p content and it's related data.
1193          $this->framework->deleteLibraryUsage($h5pid);
1194  
1195          // Get the h5p content libraries from the DB.
1196          $h5pcontentlibraries = $DB->get_record('h5p_contents_libraries', ['h5pid' => $h5pid]);
1197  
1198          // The particular h5p content libraries should no longer exist in the db.
1199          $this->assertEmpty($h5pcontentlibraries);
1200      }
1201  
1202      /**
1203       * Test the behaviour of test_saveLibraryUsage().
1204       */
1205      public function test_saveLibraryUsage() {
1206          global $DB;
1207  
1208          $this->resetAfterTest();
1209  
1210          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1211  
1212          // Create a library 'Library'.
1213          $library = $generator->create_library_record('Library', 'Title');
1214          // Create a library 'DependencyLibrary1'.
1215          $dependency1 = $generator->create_library_record('DependencyLibrary1', 'DependencyTitle1');
1216          // Create a library 'DependencyLibrary2'.
1217          $dependency2 = $generator->create_library_record('DependencyLibrary2', 'DependencyTitle2');
1218          // Create an h5p content with 'Library' as it's main library.
1219          $contentid = $generator->create_h5p_record($library->id);
1220  
1221          $dependencies = array(
1222              array(
1223                  'library' => array(
1224                      'libraryId' => $dependency1->id,
1225                      'machineName' => $dependency1->machinename,
1226                      'dropLibraryCss' => $dependency1->droplibrarycss
1227                  ),
1228                  'type' => 'preloaded',
1229                  'weight' => 1
1230              ),
1231              array(
1232                  'library' => array(
1233                      'libraryId' => $dependency2->id,
1234                      'machineName' => $dependency2->machinename,
1235                      'dropLibraryCss' => $dependency2->droplibrarycss
1236                  ),
1237                  'type' => 'preloaded',
1238                  'weight' => 2
1239              ),
1240          );
1241  
1242          // Save 'DependencyLibrary1' and 'DependencyLibrary2' as h5p content libraries.
1243          $this->framework->saveLibraryUsage($contentid, $dependencies);
1244  
1245          // Get the h5p content libraries from the DB.
1246          $libdependencies = $DB->get_records('h5p_contents_libraries', ['h5pid' => $contentid], 'id ASC');
1247  
1248          // Make sure that 'DependencyLibrary1' and 'DependencyLibrary2' are properly set as h5p content libraries.
1249          $this->assertEquals(2, count($libdependencies));
1250          $this->assertEquals($dependency1->id, reset($libdependencies)->libraryid);
1251          $this->assertEquals($dependency2->id, end($libdependencies)->libraryid);
1252      }
1253  
1254      /**
1255       * Test the behaviour of getLibraryUsage() without skipping a particular h5p content.
1256       */
1257      public function test_getLibraryUsage_no_skip_content() {
1258          $this->resetAfterTest();
1259  
1260          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1261  
1262          // Generate h5p related data.
1263          $generateddata = $generator->generate_h5p_data();
1264          // The Id of the library 'Library1'.
1265          $library1id = $generateddata->lib1->data->id;
1266          // The Id of the library 'Library2'.
1267          $library2id = $generateddata->lib2->data->id;
1268          // The Id of the library 'Library5'.
1269          $library5id = $generateddata->lib5->data->id;
1270  
1271          // Get the library usage for 'Library1' (do not skip content).
1272          $data = $this->framework->getLibraryUsage($library1id);
1273  
1274          $expected = array(
1275              'content' => 1,
1276              'libraries' => 1
1277          );
1278  
1279          // Make sure 'Library1' is used by 1 content and is a dependency to 1 library.
1280          $this->assertEquals($expected, $data);
1281  
1282          // Get the library usage for 'Library2' (do not skip content).
1283          $data = $this->framework->getLibraryUsage($library2id);
1284  
1285          $expected = array(
1286              'content' => 1,
1287              'libraries' => 2,
1288          );
1289  
1290          // Make sure 'Library2' is used by 1 content and is a dependency to 2 libraries.
1291          $this->assertEquals($expected, $data);
1292  
1293           // Get the library usage for 'Library5' (do not skip content).
1294          $data = $this->framework->getLibraryUsage($library5id);
1295  
1296          $expected = array(
1297              'content' => 0,
1298              'libraries' => 1,
1299          );
1300  
1301          // Make sure 'Library5' is not used by any content and is a dependency to 1 library.
1302          $this->assertEquals($expected, $data);
1303      }
1304  
1305      /**
1306       * Test the behaviour of getLibraryUsage() when skipping a particular content.
1307       */
1308      public function test_getLibraryUsage_skip_content() {
1309          $this->resetAfterTest();
1310  
1311          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1312  
1313          // Generate h5p related data.
1314          $generateddata = $generator->generate_h5p_data();
1315          // The Id of the library 'Library1'.
1316          $library1id = $generateddata->lib1->data->id;
1317  
1318          // Get the library usage for 'Library1' (skip content).
1319          $data = $this->framework->getLibraryUsage($library1id, true);
1320          $expected = array(
1321              'content' => -1,
1322              'libraries' => 1,
1323          );
1324  
1325          // Make sure 'Library1' is a dependency to 1 library.
1326          $this->assertEquals($expected, $data);
1327      }
1328  
1329      /**
1330       * Test the behaviour of loadLibrary() when requesting an existing library.
1331       */
1332      public function test_loadLibrary_existing_library() {
1333          $this->resetAfterTest();
1334  
1335          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1336  
1337          // Generate h5p related data.
1338          $generateddata = $generator->generate_h5p_data();
1339          // The library data of 'Library1'.
1340          $library1 = $generateddata->lib1->data;
1341          // The library data of 'Library5'.
1342          $library5 = $generateddata->lib5->data;
1343  
1344          // The preloaded dependencies.
1345          $preloadeddependencies = array();
1346  
1347          foreach ($generateddata->lib1->dependencies as $preloadeddependency) {
1348              $preloadeddependencies[] = array(
1349                  'machineName' => $preloadeddependency->machinename,
1350                  'majorVersion' => $preloadeddependency->majorversion,
1351                  'minorVersion' => $preloadeddependency->minorversion
1352              );
1353          }
1354  
1355          // Create a dynamic dependency.
1356          $generator->create_library_dependency_record($library1->id, $library5->id, 'dynamic');
1357  
1358          $dynamicdependencies[] = array(
1359              'machineName' => $library5->machinename,
1360              'majorVersion' => $library5->majorversion,
1361              'minorVersion' => $library5->minorversion
1362          );
1363  
1364          // Load 'Library1' data.
1365          $data = $this->framework->loadLibrary($library1->machinename, $library1->majorversion,
1366              $library1->minorversion);
1367  
1368          $expected = array(
1369              'libraryId' => $library1->id,
1370              'title' => $library1->title,
1371              'machineName' => $library1->machinename,
1372              'majorVersion' => $library1->majorversion,
1373              'minorVersion' => $library1->minorversion,
1374              'patchVersion' => $library1->patchversion,
1375              'runnable' => $library1->runnable,
1376              'fullscreen' => $library1->fullscreen,
1377              'embedTypes' => $library1->embedtypes,
1378              'preloadedJs' => $library1->preloadedjs,
1379              'preloadedCss' => $library1->preloadedcss,
1380              'dropLibraryCss' => $library1->droplibrarycss,
1381              'semantics' => $library1->semantics,
1382              'preloadedDependencies' => $preloadeddependencies,
1383              'dynamicDependencies' => $dynamicdependencies
1384          );
1385  
1386          // Make sure the 'Library1' data is properly loaded.
1387          $this->assertEquals($expected, $data);
1388      }
1389  
1390      /**
1391       * Test the behaviour of loadLibrary() when requesting a non-existent library.
1392       */
1393      public function test_loadLibrary_non_existent_library() {
1394          $this->resetAfterTest();
1395  
1396          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1397  
1398          // Generate h5p related data.
1399          $generator->generate_h5p_data();
1400  
1401          // Attempt to load a non-existent library.
1402          $data = $this->framework->loadLibrary('MissingLibrary', 1, 2);
1403  
1404          // Make sure nothing is loaded.
1405          $this->assertFalse($data);
1406      }
1407  
1408      /**
1409       * Test the behaviour of loadLibrarySemantics().
1410       *
1411       * @dataProvider loadLibrarySemantics_provider
1412       * @param array $libraryrecords Array containing data for the library creation
1413       * @param array $testlibrary Array containing the test library data
1414       * @param string $expected The expected semantics value
1415       **/
1416      public function test_loadLibrarySemantics(array $libraryrecords, array $testlibrary, string $expected): void {
1417          $this->resetAfterTest();
1418  
1419          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1420  
1421          foreach ($libraryrecords as $library) {
1422              call_user_func_array([$generator, 'create_library_record'], $library);
1423          }
1424  
1425          $this->assertEquals($expected, $this->framework->loadLibrarySemantics(
1426              $testlibrary['machinename'], $testlibrary['majorversion'], $testlibrary['minorversion']));
1427      }
1428  
1429      /**
1430       * Data provider for test_loadLibrarySemantics().
1431       *
1432       * @return array
1433       */
1434      public function loadLibrarySemantics_provider(): array {
1435  
1436          $semantics = json_encode(
1437              [
1438                  'type' => 'text',
1439                  'name' => 'text',
1440                  'label' => 'Plain text',
1441                  'description' => 'Please add some text'
1442              ]
1443          );
1444  
1445          return [
1446              'Library with semantics' => [
1447                  [
1448                      ['Library1', 'Lib1', 1, 1, 2, $semantics],
1449                  ],
1450                  [
1451                      'machinename' => 'Library1',
1452                      'majorversion' => 1,
1453                      'minorversion' => 1
1454                  ],
1455                  $semantics,
1456              ],
1457              'Library without semantics' => [
1458                  [
1459                      ['Library2', 'Lib2', 1, 2, 2, ''],
1460                  ],
1461                  [
1462                      'machinename' => 'Library2',
1463                      'majorversion' => 1,
1464                      'minorversion' => 2
1465                  ],
1466                  '',
1467              ]
1468          ];
1469      }
1470  
1471      /**
1472       * Test the behaviour of alterLibrarySemantics().
1473       */
1474      public function test_alterLibrarySemantics() {
1475          global $DB;
1476  
1477          $this->resetAfterTest();
1478  
1479          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1480  
1481          $semantics = json_encode(
1482              array(
1483                  'type' => 'text',
1484                  'name' => 'text',
1485                  'label' => 'Plain text',
1486                  'description' => 'Please add some text'
1487              )
1488          );
1489  
1490          // Create a library 'Library1' with semantics.
1491          $library1 = $generator->create_library_record('Library1', 'Lib1', 1, 1, 2, $semantics);
1492  
1493          $updatedsemantics = array(
1494              'type' => 'text',
1495              'name' => 'updated text',
1496              'label' => 'Updated text',
1497              'description' => 'Please add some text'
1498          );
1499  
1500          // Alter the semantics of 'Library1'.
1501          $this->framework->alterLibrarySemantics($updatedsemantics, 'Library1', 1, 1);
1502  
1503          // Get the semantics of 'Library1' from the DB.
1504          $currentsemantics = $DB->get_field('h5p_libraries', 'semantics', array('id' => $library1->id));
1505  
1506          // The semantics for Library1 shouldn't be updated.
1507          $this->assertEquals($semantics, $currentsemantics);
1508      }
1509  
1510      /**
1511       * Test the behaviour of deleteLibraryDependencies() when requesting to delete the
1512       * dependencies of an existing library.
1513       */
1514      public function test_deleteLibraryDependencies_existing_library() {
1515          global $DB;
1516  
1517          $this->resetAfterTest();
1518  
1519          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1520  
1521          // Generate h5p related data.
1522          $data = $generator->generate_h5p_data();
1523          // The data of the library 'Library1'.
1524          $library1 = $data->lib1->data;
1525  
1526          // Get the dependencies of 'Library1'.
1527          $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1528          // The 'Library1' should have 3 dependencies ('Library2', 'Library3', 'Library4').
1529          $this->assertCount(3, $dependencies);
1530  
1531          // Delete the dependencies of 'Library1'.
1532          $this->framework->deleteLibraryDependencies($library1->id);
1533  
1534          $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1535          // The 'Library1' should have 0 dependencies.
1536          $this->assertCount(0, $dependencies);
1537      }
1538  
1539      /**
1540       * Test the behaviour of deleteLibraryDependencies() when requesting to delete the
1541       * dependencies of a non-existent library.
1542       */
1543      public function test_deleteLibraryDependencies_non_existent_library() {
1544          global $DB;
1545  
1546          $this->resetAfterTest();
1547  
1548          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1549  
1550          // Generate h5p related data.
1551          $data = $generator->generate_h5p_data();
1552          // The data of the library 'Library1'.
1553          $library1 = $data->lib1->data;
1554  
1555          // Get the dependencies of 'Library1'.
1556          $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1557          // The 'Library1' should have 3 dependencies ('Library2', 'Library3', 'Library4').
1558          $this->assertCount(3, $dependencies);
1559  
1560          // Delete the dependencies of a non-existent library.
1561          $this->framework->deleteLibraryDependencies(0);
1562  
1563          $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1564          // The 'Library1' should have 3 dependencies.
1565          $this->assertCount(3, $dependencies);
1566      }
1567  
1568      /**
1569       * Test the behaviour of deleteLibrary().
1570       */
1571      public function test_deleteLibrary() {
1572          global $DB;
1573  
1574          $this->resetAfterTest();
1575  
1576          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1577  
1578          // Generate h5p related data.
1579          $data = $generator->generate_h5p_data(true);
1580          // The data of the 'Library1' library.
1581          $library1 = $data->lib1->data;
1582  
1583          // Get the library dependencies of 'Library1'.
1584          $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1585  
1586          // The 'Library1' should have 3 library dependencies ('Library2', 'Library3', 'Library4').
1587          $this->assertCount(3, $dependencies);
1588  
1589          // Return the created 'Library1' files.
1590          $libraryfiles = $DB->get_records('files',
1591              array(
1592                  'component' => \core_h5p\file_storage::COMPONENT,
1593                  'filearea' => \core_h5p\file_storage::LIBRARY_FILEAREA,
1594                  'itemid' => $library1->id
1595              )
1596          );
1597  
1598          // The library ('Library1') should have 7 related folders/files.
1599          $this->assertCount(7, $libraryfiles);
1600  
1601          // Delete the library.
1602          $this->framework->deleteLibrary($library1);
1603  
1604          $lib1 = $DB->get_record('h5p_libraries', ['machinename' => $library1->machinename]);
1605          $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1606          $libraryfiles = $DB->get_records('files',
1607              array(
1608                  'component' => \core_h5p\file_storage::COMPONENT,
1609                  'filearea' => \core_h5p\file_storage::LIBRARY_FILEAREA,
1610                  'itemid' => $library1->id
1611              )
1612          );
1613  
1614          // The 'Library1' should not exist.
1615          $this->assertEmpty($lib1);
1616          // The library ('Library1')  should have 0 dependencies.
1617          $this->assertCount(0, $dependencies);
1618          // The library (library1) should have 0 related folders/files.
1619          $this->assertCount(0, $libraryfiles);
1620      }
1621  
1622      /**
1623       * Test the behaviour of loadContent().
1624       */
1625      public function test_loadContent() {
1626          global $DB;
1627  
1628          $this->resetAfterTest();
1629  
1630          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1631  
1632          // Generate h5p related data.
1633          $data = $generator->generate_h5p_data();
1634          // The Id of the created h5p content.
1635          $h5pid = $data->h5pcontent->h5pid;
1636          // Get the h5p content data from the DB.
1637          $h5p = $DB->get_record('h5p', ['id' => $h5pid]);
1638          // The data of content's main library ('MainLibrary').
1639          $mainlibrary = $data->mainlib->data;
1640  
1641          // Load the h5p content.
1642          $content = $this->framework->loadContent($h5pid);
1643  
1644          $expected = array(
1645              'id' => $h5p->id,
1646              'params' => $h5p->jsoncontent,
1647              'embedType' => 'iframe',
1648              'disable' => $h5p->displayoptions,
1649              'title' => $mainlibrary->title,
1650              'slug' => H5PCore::slugify($mainlibrary->title) . '-' . $h5p->id,
1651              'filtered' => $h5p->filtered,
1652              'libraryId' => $mainlibrary->id,
1653              'libraryName' => $mainlibrary->machinename,
1654              'libraryMajorVersion' => $mainlibrary->majorversion,
1655              'libraryMinorVersion' => $mainlibrary->minorversion,
1656              'libraryEmbedTypes' => $mainlibrary->embedtypes,
1657              'libraryFullscreen' => $mainlibrary->fullscreen,
1658              'metadata' => '',
1659              'pathnamehash' => $h5p->pathnamehash
1660          );
1661  
1662          $params = json_decode($h5p->jsoncontent);
1663          if (empty($params->metadata)) {
1664              $params->metadata = new \stdClass();
1665          }
1666          $expected['metadata'] = $params->metadata;
1667          $expected['params'] = json_encode($params->params ?? $params);
1668  
1669          // The returned content should match the expected array.
1670          $this->assertEquals($expected, $content);
1671      }
1672  
1673      /**
1674       * Test the behaviour of loadContentDependencies() when requesting content dependencies
1675       * without specifying the dependency type.
1676       */
1677      public function test_loadContentDependencies_no_type_defined() {
1678          $this->resetAfterTest();
1679  
1680          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1681  
1682          // Generate h5p related data.
1683          $data = $generator->generate_h5p_data();
1684          // The Id of the h5p content.
1685          $h5pid = $data->h5pcontent->h5pid;
1686          // The content dependencies.
1687          $dependencies = $data->h5pcontent->contentdependencies;
1688  
1689          // Add Library5 as a content dependency (dynamic dependency type).
1690          $library5 = $data->lib5->data;
1691          $generator->create_contents_libraries_record($h5pid, $library5->id, 'dynamic');
1692  
1693          // Get all content dependencies.
1694          $contentdependencies = $this->framework->loadContentDependencies($h5pid);
1695  
1696          $expected = array();
1697          foreach ($dependencies as $dependency) {
1698              $expected[$dependency->machinename] = array(
1699                  'libraryId' => $dependency->id,
1700                  'machineName' => $dependency->machinename,
1701                  'majorVersion' => $dependency->majorversion,
1702                  'minorVersion' => $dependency->minorversion,
1703                  'patchVersion' => $dependency->patchversion,
1704                  'preloadedCss' => $dependency->preloadedcss,
1705                  'preloadedJs' => $dependency->preloadedjs,
1706                  'dropCss' => '0',
1707                  'dependencyType' => 'preloaded'
1708              );
1709          }
1710  
1711          $expected = array_merge($expected,
1712              array(
1713                  'Library5' => array(
1714                      'libraryId' => $library5->id,
1715                      'machineName' => $library5->machinename,
1716                      'majorVersion' => $library5->majorversion,
1717                      'minorVersion' => $library5->minorversion,
1718                      'patchVersion' => $library5->patchversion,
1719                      'preloadedCss' => $library5->preloadedcss,
1720                      'preloadedJs' => $library5->preloadedjs,
1721                      'dropCss' => '0',
1722                      'dependencyType' => 'dynamic'
1723                  )
1724              )
1725          );
1726  
1727          // The loaded content dependencies should return 6 libraries.
1728          $this->assertCount(6, $contentdependencies);
1729          $this->assertEquals($expected, $contentdependencies);
1730      }
1731  
1732      /**
1733       * Test the behaviour of loadContentDependencies() when requesting content dependencies
1734       * with specifying the dependency type.
1735       */
1736      public function test_loadContentDependencies_type_defined() {
1737          $this->resetAfterTest();
1738  
1739          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1740  
1741          // Generate h5p related data.
1742          $data = $generator->generate_h5p_data();
1743          // The Id of the h5p content.
1744          $h5pid = $data->h5pcontent->h5pid;
1745          // The content dependencies.
1746          $dependencies = $data->h5pcontent->contentdependencies;
1747  
1748          // Add Library5 as a content dependency (dynamic dependency type).
1749          $library5 = $data->lib5->data;
1750          $generator->create_contents_libraries_record($h5pid, $library5->id, 'dynamic');
1751  
1752          // Load all content dependencies of dependency type 'preloaded'.
1753          $preloadeddependencies = $this->framework->loadContentDependencies($h5pid, 'preloaded');
1754  
1755          $expected = array();
1756          foreach ($dependencies as $dependency) {
1757              $expected[$dependency->machinename] = array(
1758                  'libraryId' => $dependency->id,
1759                  'machineName' => $dependency->machinename,
1760                  'majorVersion' => $dependency->majorversion,
1761                  'minorVersion' => $dependency->minorversion,
1762                  'patchVersion' => $dependency->patchversion,
1763                  'preloadedCss' => $dependency->preloadedcss,
1764                  'preloadedJs' => $dependency->preloadedjs,
1765                  'dropCss' => '0',
1766                  'dependencyType' => 'preloaded'
1767              );
1768          }
1769  
1770          // The loaded content dependencies should return 5 libraries.
1771          $this->assertCount(5, $preloadeddependencies);
1772          $this->assertEquals($expected, $preloadeddependencies);
1773  
1774          // Load all content dependencies of dependency type 'dynamic'.
1775          $dynamicdependencies = $this->framework->loadContentDependencies($h5pid, 'dynamic');
1776  
1777          $expected = array(
1778              'Library5' => array(
1779                  'libraryId' => $library5->id,
1780                  'machineName' => $library5->machinename,
1781                  'majorVersion' => $library5->majorversion,
1782                  'minorVersion' => $library5->minorversion,
1783                  'patchVersion' => $library5->patchversion,
1784                  'preloadedCss' => $library5->preloadedcss,
1785                  'preloadedJs' => $library5->preloadedjs,
1786                  'dropCss' => '0',
1787                  'dependencyType' => 'dynamic'
1788              )
1789          );
1790  
1791          // The loaded content dependencies should now return 1 library.
1792          $this->assertCount(1, $dynamicdependencies);
1793          $this->assertEquals($expected, $dynamicdependencies);
1794      }
1795  
1796      /**
1797       * Test the behaviour of getOption().
1798       */
1799      public function test_getOption(): void {
1800          $this->resetAfterTest();
1801  
1802          // Get value for display_option_download.
1803          $value = $this->framework->getOption(H5PCore::DISPLAY_OPTION_DOWNLOAD);
1804          $expected = H5PDisplayOptionBehaviour::CONTROLLED_BY_AUTHOR_DEFAULT_OFF;
1805          $this->assertEquals($expected, $value);
1806  
1807          // Get value for display_option_embed using default value (it should be ignored).
1808          $value = $this->framework->getOption(H5PCore::DISPLAY_OPTION_EMBED, H5PDisplayOptionBehaviour::NEVER_SHOW);
1809          $expected = H5PDisplayOptionBehaviour::CONTROLLED_BY_AUTHOR_DEFAULT_OFF;
1810          $this->assertEquals($expected, $value);
1811  
1812          // Get value for unexisting setting without default.
1813          $value = $this->framework->getOption('unexistingsetting');
1814          $expected = false;
1815          $this->assertEquals($expected, $value);
1816  
1817          // Get value for unexisting setting with default.
1818          $value = $this->framework->getOption('unexistingsetting', 'defaultvalue');
1819          $expected = 'defaultvalue';
1820          $this->assertEquals($expected, $value);
1821      }
1822  
1823      /**
1824       * Test the behaviour of setOption().
1825       */
1826      public function test_setOption(): void {
1827          $this->resetAfterTest();
1828  
1829          // Set value for 'newsetting' setting.
1830          $name = 'newsetting';
1831          $value = $this->framework->getOption($name);
1832          $this->assertEquals(false, $value);
1833          $newvalue = 'value1';
1834          $this->framework->setOption($name, $newvalue);
1835          $value = $this->framework->getOption($name);
1836          $this->assertEquals($newvalue, $value);
1837  
1838          // Set value for display_option_download and then get it again. Check it hasn't changed.
1839          $name = H5PCore::DISPLAY_OPTION_DOWNLOAD;
1840          $newvalue = H5PDisplayOptionBehaviour::NEVER_SHOW;
1841          $this->framework->setOption($name, $newvalue);
1842          $value = $this->framework->getOption($name);
1843          $expected = H5PDisplayOptionBehaviour::CONTROLLED_BY_AUTHOR_DEFAULT_OFF;
1844          $this->assertEquals($expected, $value);
1845      }
1846  
1847      /**
1848       * Test the behaviour of updateContentFields().
1849       */
1850      public function test_updateContentFields() {
1851          global $DB;
1852  
1853          $this->resetAfterTest();
1854  
1855          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1856  
1857          // Create 'Library1' library.
1858          $library1 = $generator->create_library_record('Library1', 'Lib1', 1, 1, 2);
1859          // Create 'Library2' library.
1860          $library2 = $generator->create_library_record('Library2', 'Lib2', 1, 1, 2);
1861  
1862          // Create an h5p content with 'Library1' as it's main library.
1863          $h5pid = $generator->create_h5p_record($library1->id, 'iframe');
1864  
1865          $updatedata = array(
1866              'jsoncontent' => json_encode(['value' => 'test']),
1867              'mainlibraryid' => $library2->id
1868          );
1869  
1870          // Update h5p content fields.
1871          $this->framework->updateContentFields($h5pid, $updatedata);
1872  
1873          // Get the h5p content from the DB.
1874          $h5p = $DB->get_record('h5p', ['id' => $h5pid]);
1875  
1876          $expected = json_encode(['value' => 'test']);
1877  
1878          // Make sure the h5p content fields are properly updated.
1879          $this->assertEquals($expected, $h5p->jsoncontent);
1880          $this->assertEquals($library2->id, $h5p->mainlibraryid);
1881      }
1882  
1883      /**
1884       * Test the behaviour of clearFilteredParameters().
1885       */
1886      public function test_clearFilteredParameters() {
1887          global $DB;
1888  
1889          $this->resetAfterTest();
1890  
1891          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1892  
1893          // Create 3 libraries.
1894          $library1 = $generator->create_library_record('Library1', 'Lib1', 1, 1, 2);
1895          $library2 = $generator->create_library_record('Library2', 'Lib2', 1, 1, 2);
1896          $library3 = $generator->create_library_record('Library3', 'Lib3', 1, 1, 2);
1897  
1898          // Create h5p content with 'Library1' as a main library.
1899          $h5pcontentid1 = $generator->create_h5p_record($library1->id);
1900          // Create h5p content with 'Library1' as a main library.
1901          $h5pcontentid2 = $generator->create_h5p_record($library1->id);
1902          // Create h5p content with 'Library2' as a main library.
1903          $h5pcontentid3 = $generator->create_h5p_record($library2->id);
1904          // Create h5p content with 'Library3' as a main library.
1905          $h5pcontentid4 = $generator->create_h5p_record($library3->id);
1906  
1907          $h5pcontent1 = $DB->get_record('h5p', ['id' => $h5pcontentid1]);
1908          $h5pcontent2 = $DB->get_record('h5p', ['id' => $h5pcontentid2]);
1909          $h5pcontent3 = $DB->get_record('h5p', ['id' => $h5pcontentid3]);
1910          $h5pcontent4 = $DB->get_record('h5p', ['id' => $h5pcontentid4]);
1911  
1912          // The filtered parameters should be present in each h5p content.
1913          $this->assertNotEmpty($h5pcontent1->filtered);
1914          $this->assertNotEmpty($h5pcontent2->filtered);
1915          $this->assertNotEmpty($h5pcontent3->filtered);
1916          $this->assertNotEmpty($h5pcontent4->filtered);
1917  
1918          // Clear the filtered parameters for contents that have library1 and library3 as
1919          // their main library.
1920          $this->framework->clearFilteredParameters([$library1->id, $library3->id]);
1921  
1922          $h5pcontent1 = $DB->get_record('h5p', ['id' => $h5pcontentid1]);
1923          $h5pcontent2 = $DB->get_record('h5p', ['id' => $h5pcontentid2]);
1924          $h5pcontent3 = $DB->get_record('h5p', ['id' => $h5pcontentid3]);
1925          $h5pcontent4 = $DB->get_record('h5p', ['id' => $h5pcontentid4]);
1926  
1927          // The filtered parameters should be still present only for the content that has
1928          // library 2 as a main library.
1929          $this->assertEmpty($h5pcontent1->filtered);
1930          $this->assertEmpty($h5pcontent2->filtered);
1931          $this->assertNotEmpty($h5pcontent3->filtered);
1932          $this->assertEmpty($h5pcontent4->filtered);
1933      }
1934  
1935      /**
1936       * Test the behaviour of getNumNotFiltered().
1937       */
1938      public function test_getNumNotFiltered() {
1939          global $DB;
1940  
1941          $this->resetAfterTest();
1942  
1943          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1944  
1945          // Create 3 libraries.
1946          $library1 = $generator->create_library_record('Library1', 'Lib1', 1, 1, 2);
1947          $library2 = $generator->create_library_record('Library2', 'Lib2', 1, 1, 2);
1948          $library3 = $generator->create_library_record('Library3', 'Lib3', 1, 1, 2);
1949  
1950          // Create h5p content with library1 as a main library.
1951          $h5pcontentid1 = $generator->create_h5p_record($library1->id);
1952          // Create h5p content with library1 as a main library.
1953          $h5pcontentid2 = $generator->create_h5p_record($library1->id);
1954          // Create h5p content with library2 as a main library.
1955          $h5pcontentid3 = $generator->create_h5p_record($library2->id);
1956          // Create h5p content with library3 as a main library.
1957          $h5pcontentid4 = $generator->create_h5p_record($library3->id);
1958  
1959          $h5pcontent1 = $DB->get_record('h5p', ['id' => $h5pcontentid1]);
1960          $h5pcontent2 = $DB->get_record('h5p', ['id' => $h5pcontentid2]);
1961          $h5pcontent3 = $DB->get_record('h5p', ['id' => $h5pcontentid3]);
1962          $h5pcontent4 = $DB->get_record('h5p', ['id' => $h5pcontentid4]);
1963  
1964          // The filtered parameters should be present in each h5p content.
1965          $this->assertNotEmpty($h5pcontent1->filtered);
1966          $this->assertNotEmpty($h5pcontent2->filtered);
1967          $this->assertNotEmpty($h5pcontent3->filtered);
1968          $this->assertNotEmpty($h5pcontent4->filtered);
1969  
1970          // Clear the filtered parameters for contents that have library1 and library3 as
1971          // their main library.
1972          $this->framework->clearFilteredParameters([$library1->id, $library3->id]);
1973  
1974          $countnotfiltered = $this->framework->getNumNotFiltered();
1975  
1976          // 3 contents don't have their parameters filtered.
1977          $this->assertEquals(3, $countnotfiltered);
1978      }
1979  
1980      /**
1981       * Test the behaviour of getNumContent().
1982       */
1983      public function test_getNumContent() {
1984          $this->resetAfterTest();
1985  
1986          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1987  
1988          // Generate h5p related data.
1989          $data = $generator->generate_h5p_data();
1990  
1991          // The 'MainLibrary' library data.
1992          $mainlibrary = $data->mainlib->data;
1993  
1994          // The 'Library1' library data.
1995          $library1 = $data->lib1->data;
1996  
1997          // Create new h5p content with MainLibrary as a main library.
1998          $generator->create_h5p_record($mainlibrary->id);
1999  
2000          // Get the number of h5p contents that are using 'MainLibrary' as their main library.
2001          $countmainlib = $this->framework->getNumContent($mainlibrary->id);
2002  
2003          // Get the number of h5p contents that are using 'Library1' as their main library.
2004          $countlib1 = $this->framework->getNumContent($library1->id);
2005  
2006          // Make sure that 2 contents are using MainLibrary as their main library.
2007          $this->assertEquals(2, $countmainlib);
2008          // Make sure that 0 contents are using Library1 as their main library.
2009          $this->assertEquals(0, $countlib1);
2010      }
2011  
2012      /**
2013       * Test the behaviour of getNumContent() when certain contents are being skipped.
2014       */
2015      public function test_getNumContent_skip_content() {
2016          $this->resetAfterTest();
2017  
2018          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
2019  
2020          // Generate h5p related data.
2021          $data = $generator->generate_h5p_data();
2022  
2023          // The 'MainLibrary' library data.
2024          $mainlibrary = $data->mainlib->data;
2025  
2026          // Create new h5p content with MainLibrary as a main library.
2027          $h5pcontentid = $generator->create_h5p_record($mainlibrary->id);
2028  
2029          // Get the number of h5p contents that are using 'MainLibrary' as their main library.
2030          // Skip the newly created content $h5pcontentid.
2031          $countmainlib = $this->framework->getNumContent($mainlibrary->id, [$h5pcontentid]);
2032  
2033          // Make sure that 1 content is returned instead of 2 ($h5pcontentid being skipped).
2034          $this->assertEquals(1, $countmainlib);
2035      }
2036  
2037      /**
2038       * Test the behaviour of isContentSlugAvailable().
2039       */
2040      public function test_isContentSlugAvailable() {
2041          $this->resetAfterTest();
2042  
2043          $slug = 'h5p-test-slug-1';
2044  
2045          // Currently this returns always true. The slug is generated as a unique value for
2046          // each h5p content and it is not stored in the h5p content table.
2047          $isslugavailable = $this->framework->isContentSlugAvailable($slug);
2048  
2049          $this->assertTrue($isslugavailable);
2050      }
2051  
2052      /**
2053       * Test that a record is stored for cached assets.
2054       */
2055      public function test_saveCachedAssets() {
2056          global $DB;
2057  
2058          $this->resetAfterTest();
2059  
2060          $libraries = array(
2061              array(
2062                  'machineName' => 'H5P.TestLib',
2063                  'libraryId' => 405,
2064              ),
2065              array(
2066                  'FontAwesome' => 'FontAwesome',
2067                  'libraryId' => 406,
2068              ),
2069              array(
2070                  'machineName' => 'H5P.SecondLib',
2071                  'libraryId' => 407,
2072              ),
2073          );
2074  
2075          $key = 'testhashkey';
2076  
2077          $this->framework->saveCachedAssets($key, $libraries);
2078  
2079          $records = $DB->get_records('h5p_libraries_cachedassets');
2080  
2081          $this->assertCount(3, $records);
2082      }
2083  
2084      /**
2085       * Test that the correct libraries are removed from the cached assets table
2086       */
2087      public function test_deleteCachedAssets() {
2088          global $DB;
2089  
2090          $this->resetAfterTest();
2091  
2092          $libraries = array(
2093              array(
2094                  'machineName' => 'H5P.TestLib',
2095                  'libraryId' => 405,
2096              ),
2097              array(
2098                  'FontAwesome' => 'FontAwesome',
2099                  'libraryId' => 406,
2100              ),
2101              array(
2102                  'machineName' => 'H5P.SecondLib',
2103                  'libraryId' => 407,
2104              ),
2105          );
2106  
2107          $key1 = 'testhashkey';
2108          $this->framework->saveCachedAssets($key1, $libraries);
2109  
2110          $libraries = array(
2111              array(
2112                  'machineName' => 'H5P.DiffLib',
2113                  'libraryId' => 408,
2114              ),
2115              array(
2116                  'FontAwesome' => 'FontAwesome',
2117                  'libraryId' => 406,
2118              ),
2119              array(
2120                  'machineName' => 'H5P.ThirdLib',
2121                  'libraryId' => 409,
2122              ),
2123          );
2124  
2125          $key2 = 'secondhashkey';
2126          $this->framework->saveCachedAssets($key2, $libraries);
2127  
2128          $libraries = array(
2129              array(
2130                  'machineName' => 'H5P.AnotherDiffLib',
2131                  'libraryId' => 410,
2132              ),
2133              array(
2134                  'FontAwesome' => 'NotRelated',
2135                  'libraryId' => 411,
2136              ),
2137              array(
2138                  'machineName' => 'H5P.ForthLib',
2139                  'libraryId' => 412,
2140              ),
2141          );
2142  
2143          $key3 = 'threeforthewin';
2144          $this->framework->saveCachedAssets($key3, $libraries);
2145  
2146          $records = $DB->get_records('h5p_libraries_cachedassets');
2147          $this->assertCount(9, $records);
2148  
2149          // Selecting one library id will result in all related library entries also being deleted.
2150          // Going to use the FontAwesome library id. The first two hashes should be returned.
2151          $hashes = $this->framework->deleteCachedAssets(406);
2152          $this->assertCount(2, $hashes);
2153          $index = array_search($key1, $hashes);
2154          $this->assertEquals($key1, $hashes[$index]);
2155          $index = array_search($key2, $hashes);
2156          $this->assertEquals($key2, $hashes[$index]);
2157          $index = array_search($key3, $hashes);
2158          $this->assertFalse($index);
2159  
2160          // Check that the records have been removed as well.
2161          $records = $DB->get_records('h5p_libraries_cachedassets');
2162          $this->assertCount(3, $records);
2163      }
2164  
2165      /**
2166       * Test the behaviour of getLibraryContentCount().
2167       */
2168      public function test_getLibraryContentCount() {
2169          $this->resetAfterTest();
2170  
2171          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
2172  
2173          // Generate h5p related data.
2174          $data = $generator->generate_h5p_data();
2175  
2176          // The 'MainLibrary' library data.
2177          $mainlibrary = $data->mainlib->data;
2178  
2179          // The 'Library2' library data.
2180          $library2 = $data->lib2->data;
2181  
2182          // Create new h5p content with Library2 as it's main library.
2183          $generator->create_h5p_record($library2->id);
2184  
2185          // Create new h5p content with MainLibrary as it's main library.
2186          $generator->create_h5p_record($mainlibrary->id);
2187  
2188          $countlibrarycontent = $this->framework->getLibraryContentCount();
2189  
2190          $expected = array(
2191              "{$mainlibrary->machinename} {$mainlibrary->majorversion}.{$mainlibrary->minorversion}" => 2,
2192              "{$library2->machinename} {$library2->majorversion}.{$library2->minorversion}" => 1,
2193          );
2194  
2195          // MainLibrary and Library1 are currently main libraries to the existing h5p contents.
2196          // Should return the number of cases where MainLibrary and Library1 are main libraries to an h5p content.
2197          $this->assertEquals($expected, $countlibrarycontent);
2198      }
2199  
2200      /**
2201       * Test the behaviour of test_libraryHasUpgrade().
2202       *
2203       * @dataProvider libraryHasUpgrade_provider
2204       * @param array $libraryrecords Array containing data for the library creation
2205       * @param array $testlibrary Array containing the test library data
2206       * @param bool $expected The expectation whether the library is patched or not
2207       **/
2208      public function test_libraryHasUpgrade(array $libraryrecords, array $testlibrary, bool $expected): void {
2209          $this->resetAfterTest();
2210  
2211          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
2212  
2213          foreach ($libraryrecords as $library) {
2214              call_user_func_array([$generator, 'create_library_record'], $library);
2215          }
2216  
2217          $this->assertEquals($expected, $this->framework->libraryHasUpgrade($testlibrary));
2218      }
2219  
2220      /**
2221       * Data provider for test_libraryHasUpgrade().
2222       *
2223       * @return array
2224       */
2225      public function libraryHasUpgrade_provider(): array {
2226          return [
2227              'Lower major version; Identical lower version' => [
2228                  [
2229                      ['Library', 'Lib', 2, 2],
2230                  ],
2231                  [
2232                      'machineName' => 'Library',
2233                      'majorVersion' => 1,
2234                      'minorVersion' => 2
2235                  ],
2236                  true,
2237              ],
2238              'Major version identical; Lower minor version' => [
2239                  [
2240                      ['Library', 'Lib', 2, 2],
2241                  ],
2242                  [
2243                      'machineName' => 'Library',
2244                      'majorVersion' => 2,
2245                      'minorVersion' => 1
2246                  ],
2247                  true,
2248              ],
2249              'Major version identical; Minor version identical' => [
2250                  [
2251                      ['Library', 'Lib', 2, 2],
2252                  ],
2253                  [
2254                      'machineName' => 'Library',
2255                      'majorVersion' => 2,
2256                      'minorVersion' => 2
2257                  ],
2258                  false,
2259              ],
2260              'Major version higher; Minor version identical' => [
2261                  [
2262                      ['Library', 'Lib', 2, 2],
2263                  ],
2264                  [
2265                      'machineName' => 'Library',
2266                      'majorVersion' => 3,
2267                      'minorVersion' => 2
2268                  ],
2269                  false,
2270              ],
2271              'Major version identical; Minor version newer' => [
2272                  [
2273                      ['Library', 'Lib', 2, 2],
2274                  ],
2275                  [
2276                      'machineName' => 'Library',
2277                      'majorVersion' => 2,
2278                      'minorVersion' => 4
2279                  ],
2280                  false,
2281              ]
2282          ];
2283      }
2284  
2285  
2286      /**
2287       * Test the behaviour of get_latest_library_version().
2288       */
2289      public function test_get_latest_library_version() {
2290          global $DB;
2291  
2292          $this->resetAfterTest();
2293  
2294          $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
2295          // Create a library record.
2296          $machinename = 'TestLibrary';
2297          $lib1 = $generator->create_library_record($machinename, 'Test', 1, 1, 2);
2298          $lib2 = $generator->create_library_record($machinename, 'Test', 1, 2, 1);
2299  
2300          $content = array(
2301              'params' => json_encode(['param1' => 'Test']),
2302              'library' => array(
2303                  'libraryId' => 0,
2304                  'machineName' => 'TestLibrary',
2305              ),
2306              'disable' => 8
2307          );
2308  
2309          // Get the latest id (at this point, should be lib2).
2310          $latestlib = $this->framework->get_latest_library_version($machinename);
2311          $this->assertEquals($lib2->id, $latestlib->id);
2312  
2313          // Get the latest id (at this point, should be lib3).
2314          $lib3 = $generator->create_library_record($machinename, 'Test', 2, 1, 0);
2315          $latestlib = $this->framework->get_latest_library_version($machinename);
2316          $this->assertEquals($lib3->id, $latestlib->id);
2317  
2318          // Get the latest id (at this point, should be still lib3).
2319          $lib4 = $generator->create_library_record($machinename, 'Test', 1, 1, 3);
2320          $latestlib = $this->framework->get_latest_library_version($machinename);
2321          $this->assertEquals($lib3->id, $latestlib->id);
2322  
2323          // Get the latest id (at this point, should be lib5).
2324          $lib5 = $generator->create_library_record($machinename, 'Test', 2, 1, 6);
2325          $latestlib = $this->framework->get_latest_library_version($machinename);
2326          $this->assertEquals($lib5->id, $latestlib->id);
2327      }
2328  }