Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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