Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Testing the H5P H5PFileStorage interface implementation.
  19   *
  20   * @package    core_h5p
  21   * @category   test
  22   * @copyright  2019 Victor Deniz <victor@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace core_h5p\local\tests;
  27  
  28  use core_h5p\file_storage;
  29  use core_h5p\local\library\autoloader;
  30  use core_h5p\helper;
  31  use file_archive;
  32  use moodle_exception;
  33  use ReflectionMethod;
  34  use zip_archive;
  35  
  36  defined('MOODLE_INTERNAL') || die();
  37  
  38  /**
  39   * Test class covering the H5PFileStorage interface implementation.
  40   *
  41   * @package    core_h5p
  42   * @copyright  2019 Victor Deniz <victor@moodle.com>
  43   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  44   * @runTestsInSeparateProcesses
  45   */
  46  class h5p_file_storage_testcase extends \advanced_testcase {
  47  
  48      /** @var \core_h5p\file_storage H5P file storage instance */
  49      protected $h5p_file_storage;
  50      /** @var \file_storage Core Moodle file_storage associated to the H5P file_storage */
  51      protected $h5p_fs_fs;
  52      /** @var \context Moodle context of the H5P file_storage */
  53      protected $h5p_fs_context;
  54      /** @var string Path to temp directory */
  55      protected $h5p_tempath;
  56      /** @var \core_h5p_generator  H5P generator instance */
  57      protected $h5p_generator;
  58      /** @var array $files an array used in the cache tests. */
  59      protected $files = ['scripts' => [], 'styles' => []];
  60      /** @var int $libraryid an id for the library. */
  61      protected $libraryid = 1;
  62  
  63      protected function setUp() {
  64          parent::setUp();
  65          $this->resetAfterTest(true);
  66  
  67          autoloader::register();
  68  
  69          // Fetch generator.
  70          $generator = \testing_util::get_data_generator();
  71          $this->h5p_generator = $generator->get_plugin_generator('core_h5p');
  72  
  73          // Create file_storage_instance and create H5P temp directory.
  74          $this->h5p_file_storage = new file_storage();
  75          $this->h5p_tempath = $this->h5p_file_storage->getTmpPath();
  76          check_dir_exists($this->h5p_tempath);
  77  
  78          // Get value of protected properties.
  79          $h5p_fs_rc = new \ReflectionClass(file_storage::class);
  80          $h5p_file_storage_context = $h5p_fs_rc->getProperty('context');
  81          $h5p_file_storage_context->setAccessible(true);
  82          $this->h5p_fs_context = $h5p_file_storage_context->getValue($this->h5p_file_storage);
  83  
  84          $h5p_file_storage_fs = $h5p_fs_rc->getProperty('fs');
  85          $h5p_file_storage_fs->setAccessible(true);
  86          $this->h5p_fs_fs = $h5p_file_storage_fs->getValue($this->h5p_file_storage);
  87      }
  88  
  89      /**
  90       * Test that given the main directory of a library that all files are saved
  91       * into the file system.
  92       */
  93      public function test_saveLibrary(): void {
  94  
  95          $machinename = 'TestLib';
  96          $majorversion = 1;
  97          $minorversion = 0;
  98          [$lib, $files] = $this->h5p_generator->create_library($this->h5p_tempath, $this->libraryid, $machinename, $majorversion,
  99              $minorversion);
 100  
 101          // Now run the API call.
 102          $this->h5p_file_storage->saveLibrary($lib);
 103  
 104          // Check that files are in the Moodle file system.
 105          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 106              file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/", 'library.json');
 107          $filepath = "/{$machinename}-{$majorversion}.{$minorversion}/";
 108          $this->assertEquals($filepath, $file->get_filepath());
 109  
 110          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 111              file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/scripts/", 'testlib.min.js');
 112          $jsfilepath = "{$filepath}scripts/";
 113          $this->assertEquals($jsfilepath, $file->get_filepath());
 114  
 115          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 116              file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/styles/", 'testlib.min.css');
 117          $cssfilepath = "{$filepath}styles/";
 118          $this->assertEquals($cssfilepath, $file->get_filepath());
 119      }
 120  
 121      /**
 122       * Test that a content file can be saved.
 123       */
 124      public function test_saveContent(): void {
 125  
 126          $source = $this->h5p_tempath . '/' . 'content.json';
 127          $this->h5p_generator->create_file($source);
 128  
 129          $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
 130  
 131          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 132              file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
 133          $this->assertEquals(file_storage::CONTENT_FILEAREA, $file->get_filearea());
 134          $this->assertEquals('content.json', $file->get_filename());
 135          $this->assertEquals(5, $file->get_itemid());
 136      }
 137  
 138      /**
 139       * Test that content files located on the file system can be deleted.
 140       */
 141      public function test_deleteContent(): void {
 142  
 143          $source = $this->h5p_tempath . '/' . 'content.json';
 144          $this->h5p_generator->create_file($source);
 145  
 146          $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
 147  
 148          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 149              file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
 150          $this->assertEquals('content.json', $file->get_filename());
 151  
 152          // Now to delete the record.
 153          $this->h5p_file_storage->deleteContent(['id' => 5]);
 154          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 155              file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
 156          $this->assertFalse($file);
 157      }
 158  
 159      /**
 160       * Test that returning a temp path returns what is expected by the h5p library.
 161       */
 162      public function test_getTmpPath(): void {
 163  
 164          $temparray = explode('/', $this->h5p_tempath);
 165          $h5pdirectory = array_pop($temparray);
 166          $this->assertTrue(stripos($h5pdirectory, 'h5p-') === 0);
 167      }
 168  
 169      /**
 170       * Test that the content files can be exported to a specified location.
 171       */
 172      public function test_exportContent(): void {
 173  
 174          // Create a file to store.
 175          $source = $this->h5p_tempath . '/' . 'content.json';
 176          $this->h5p_generator->create_file($source);
 177  
 178          $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
 179  
 180          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 181              file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
 182          $this->assertEquals('content.json', $file->get_filename());
 183  
 184          // Now export it.
 185          $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
 186          check_dir_exists($destinationdirectory);
 187  
 188          $this->h5p_file_storage->exportContent(5, $destinationdirectory);
 189          // Check that there is a file now in that directory.
 190          $contents = scandir($destinationdirectory);
 191          $value = array_search('content.json', $contents);
 192          $this->assertEquals('content.json', $contents[$value]);
 193      }
 194  
 195      /**
 196       * Test that libraries on the file system can be exported to a specified location.
 197       */
 198      public function test_exportLibrary(): void {
 199  
 200          $machinename = 'TestLib';
 201          $majorversion = 1;
 202          $minorversion = 0;
 203          [$lib, $files] = $this->h5p_generator->create_library($this->h5p_tempath, $this->libraryid, $machinename, $majorversion,
 204          $minorversion);
 205  
 206          // Now run the API call.
 207          $this->h5p_file_storage->saveLibrary($lib);
 208  
 209          $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
 210          check_dir_exists($destinationdirectory);
 211  
 212          $this->h5p_file_storage->exportLibrary($lib, $destinationdirectory);
 213  
 214          $filepath = "/{$machinename}-{$majorversion}.{$minorversion}/";
 215          // There should be at least three items here (but could be more with . and ..).
 216          $this->assertFileExists($destinationdirectory . $filepath . 'library.json');
 217          $this->assertFileExists($destinationdirectory . $filepath . 'scripts/' . 'testlib.min.js');
 218          $this->assertFileExists($destinationdirectory . $filepath . 'styles/' . 'testlib.min.css');
 219      }
 220  
 221      /**
 222       * Test that an export file can be saved into the file system.
 223       */
 224      public function test_saveExport(): void {
 225  
 226          $filename = 'someexportedfile.h5p';
 227          $source = $this->h5p_tempath . '/' . $filename;
 228          $this->h5p_generator->create_file($source);
 229  
 230          $this->h5p_file_storage->saveExport($source, $filename);
 231  
 232          // Check out if the file is there.
 233          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 234              file_storage::EXPORT_FILEAREA, '0', '/', $filename);
 235          $this->assertEquals(file_storage::EXPORT_FILEAREA, $file->get_filearea());
 236      }
 237  
 238      /**
 239       * Test that an exort file can be deleted from the file system.
 240       * @return [type] [description]
 241       */
 242      public function test_deleteExport(): void {
 243  
 244          $filename = 'someexportedfile.h5p';
 245          $source = $this->h5p_tempath . '/' . $filename;
 246          $this->h5p_generator->create_file($source);
 247  
 248          $this->h5p_file_storage->saveExport($source, $filename);
 249  
 250          // Check out if the file is there.
 251          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 252              file_storage::EXPORT_FILEAREA, '0', '/', $filename);
 253          $this->assertEquals(file_storage::EXPORT_FILEAREA, $file->get_filearea());
 254  
 255          // Time to delete.
 256          $this->h5p_file_storage->deleteExport($filename);
 257  
 258          // Check out if the file is there.
 259          $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
 260              file_storage::EXPORT_FILEAREA, '0', '/', $filename);
 261          $this->assertFalse($file);
 262      }
 263  
 264      /**
 265       * Test to check if an export file already exists on the file system.
 266       */
 267      public function test_hasExport(): void {
 268  
 269          $filename = 'someexportedfile.h5p';
 270          $source = $this->h5p_tempath . '/' . $filename;
 271          $this->h5p_generator->create_file($source);
 272  
 273          // Check that it doesn't exist in the file system.
 274          $this->assertFalse($this->h5p_file_storage->hasExport($filename));
 275  
 276          $this->h5p_file_storage->saveExport($source, $filename);
 277          // Now it should be present.
 278          $this->assertTrue($this->h5p_file_storage->hasExport($filename));
 279      }
 280  
 281      /**
 282       * Test that all the library files for an H5P activity can be concatenated into "cache" files. One for js and another for css.
 283       */
 284      public function test_cacheAssets(): void {
 285  
 286          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 287  
 288          $machinename = 'TestLib';
 289          $majorversion = 1;
 290          $minorversion = 0;
 291          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 292              $minorversion);
 293          array_push($this->files['scripts'], ...$libfiles['scripts']);
 294          array_push($this->files['styles'], ...$libfiles['styles']);
 295  
 296          // Now run the API call.
 297          $this->h5p_file_storage->saveLibrary($lib);
 298  
 299          // Second library.
 300          $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
 301  
 302          $this->libraryid++;
 303          $machinename = 'SuperTest';
 304          $majorversion = 2;
 305          $minorversion = 4;
 306          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 307              $minorversion);
 308          array_push($this->files['scripts'], ...$libfiles['scripts']);
 309          array_push($this->files['styles'], ...$libfiles['styles']);
 310  
 311          $this->h5p_file_storage->saveLibrary($lib);
 312  
 313          $this->assertCount(2, $this->files['scripts']);
 314          $this->assertCount(2, $this->files['styles']);
 315  
 316          $key = 'testhashkey';
 317  
 318          $this->h5p_file_storage->cacheAssets($this->files, $key);
 319          $this->assertCount(1, $this->files['scripts']);
 320          $this->assertCount(1, $this->files['styles']);
 321  
 322  
 323          $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.js';
 324          $this->assertEquals($expectedfile, $this->files['scripts'][0]->path);
 325          $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.css';
 326          $this->assertEquals($expectedfile, $this->files['styles'][0]->path);
 327      }
 328  
 329      /**
 330       * Test that cached files can be retrieved via a key.
 331       */
 332      public function test_getCachedAssets() {
 333  
 334          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 335  
 336          $machinename = 'TestLib';
 337          $majorversion = 1;
 338          $minorversion = 0;
 339          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 340              $minorversion);
 341          array_push($this->files['scripts'], ...$libfiles['scripts']);
 342          array_push($this->files['styles'], ...$libfiles['styles']);
 343  
 344          // Now run the API call.
 345          $this->h5p_file_storage->saveLibrary($lib);
 346  
 347          // Second library.
 348          $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
 349  
 350          $this->libraryid++;
 351          $machinename = 'SuperTest';
 352          $majorversion = 2;
 353          $minorversion = 4;
 354          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 355              $minorversion);
 356          array_push($this->files['scripts'], ...$libfiles['scripts']);
 357          array_push($this->files['styles'], ...$libfiles['styles']);
 358  
 359          $this->h5p_file_storage->saveLibrary($lib);
 360  
 361          $this->assertCount(2, $this->files['scripts']);
 362          $this->assertCount(2, $this->files['styles']);
 363  
 364          $key = 'testhashkey';
 365  
 366          $this->h5p_file_storage->cacheAssets($this->files, $key);
 367  
 368          $testarray = $this->h5p_file_storage->getCachedAssets($key);
 369          $this->assertCount(1, $testarray['scripts']);
 370          $this->assertCount(1, $testarray['styles']);
 371          $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.js';
 372          $this->assertEquals($expectedfile, $testarray['scripts'][0]->path);
 373          $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.css';
 374          $this->assertEquals($expectedfile, $testarray['styles'][0]->path);
 375      }
 376  
 377      /**
 378       * Test that cache files in the files system can be removed.
 379       */
 380      public function test_deleteCachedAssets(): void {
 381          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 382  
 383          $machinename = 'TestLib';
 384          $majorversion = 1;
 385          $minorversion = 0;
 386          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 387              $minorversion);
 388          array_push($this->files['scripts'], ...$libfiles['scripts']);
 389          array_push($this->files['styles'], ...$libfiles['styles']);
 390  
 391          // Now run the API call.
 392          $this->h5p_file_storage->saveLibrary($lib);
 393  
 394          // Second library.
 395          $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
 396  
 397          $this->libraryid++;
 398          $machinename = 'SuperTest';
 399          $majorversion = 2;
 400          $minorversion = 4;
 401          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 402              $minorversion);
 403          array_push($this->files['scripts'], ...$libfiles['scripts']);
 404          array_push($this->files['styles'], ...$libfiles['styles']);
 405  
 406          $this->h5p_file_storage->saveLibrary($lib);
 407  
 408          $this->assertCount(2, $this->files['scripts']);
 409          $this->assertCount(2, $this->files['styles']);
 410  
 411          $key = 'testhashkey';
 412  
 413          $this->h5p_file_storage->cacheAssets($this->files, $key);
 414  
 415          $testarray = $this->h5p_file_storage->getCachedAssets($key);
 416          $this->assertCount(1, $testarray['scripts']);
 417          $this->assertCount(1, $testarray['styles']);
 418  
 419          // Time to delete.
 420          $this->h5p_file_storage->deleteCachedAssets([$key]);
 421          $testarray = $this->h5p_file_storage->getCachedAssets($key);
 422          $this->assertNull($testarray);
 423      }
 424  
 425      /**
 426       * Retrieve content from a file given a specific path.
 427       */
 428      public function test_getContent() {
 429          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 430  
 431          $machinename = 'TestLib';
 432          $majorversion = 1;
 433          $minorversion = 0;
 434          [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 435              $minorversion);
 436          array_push($this->files['scripts'], ...$libfiles['scripts']);
 437          array_push($this->files['styles'], ...$libfiles['styles']);
 438  
 439          // Now run the API call.
 440          $this->h5p_file_storage->saveLibrary($lib);
 441  
 442          $content = $this->h5p_file_storage->getContent($this->files['scripts'][0]->path);
 443          // The file content is created based on the file system path (\core_h5p_generator::create_file).
 444          $expectedcontent = hash("md5", $basedirectory. '/' . 'scripts' . '/' . 'testlib.min.js');
 445  
 446          $this->assertEquals($expectedcontent, $content);
 447      }
 448  
 449      /**
 450       * Test that an upgrade script can be found on the file system.
 451       */
 452      public function test_getUpgradeScript() {
 453          // Upload an upgrade file.
 454          $machinename = 'TestLib';
 455          $majorversion = 3;
 456          $minorversion = 1;
 457          $filepath = '/' . "{$machinename}-{$majorversion}.{$minorversion}" . '/';
 458          $fs = get_file_storage();
 459          $filerecord = [
 460              'contextid' => \context_system::instance()->id,
 461              'component' => file_storage::COMPONENT,
 462              'filearea' => file_storage::LIBRARY_FILEAREA,
 463              'itemid' => 15,
 464              'filepath' => $filepath,
 465              'filename' => 'upgrade.js'
 466          ];
 467          $filestorage = new file_storage();
 468          $fs->create_file_from_string($filerecord, 'test string info');
 469          $expectedfilepath = '/' . file_storage::LIBRARY_FILEAREA . $filepath . 'upgrade.js';
 470          $this->assertEquals($expectedfilepath, $filestorage->getUpgradeScript($machinename, $majorversion, $minorversion));
 471          $this->assertNull($filestorage->getUpgradeScript($machinename, $majorversion, 7));
 472      }
 473  
 474      /**
 475       * Test that information from a source can be saved to the specified path.
 476       * The zip file has the following contents
 477       * - h5ptest
 478       * |- content
 479       * |     |- content.json
 480       * |- testFont
 481       * |     |- testfont.min.css
 482       * |- testJavaScript
 483       * |     |- testscript.min.js
 484       * |- h5p.json
 485       */
 486      public function test_saveFileFromZip() {
 487  
 488          $ziparchive = new zip_archive();
 489          $path = __DIR__ . '/fixtures/h5ptest.zip';
 490          $result = $ziparchive->open($path, file_archive::OPEN);
 491  
 492          $files = $ziparchive->list_files();
 493          foreach ($files as $file) {
 494              if (!$file->is_directory) {
 495                  $stream = $ziparchive->get_stream($file->index);
 496                  $items = explode('/', $file->pathname);
 497                  array_shift($items);
 498                  $path = implode('/', $items);
 499                  $this->h5p_file_storage->saveFileFromZip($this->h5p_tempath, $path, $stream);
 500                  $filestocheck[] = $path;
 501              }
 502          }
 503          $ziparchive->close();
 504  
 505          foreach ($filestocheck as $filetocheck) {
 506              $pathtocheck = $this->h5p_tempath .'/'. $filetocheck;
 507              $this->assertFileExists($pathtocheck);
 508          }
 509      }
 510  
 511      /**
 512       * Test that a library is fully deleted from the file system
 513       */
 514      public function test_delete_library() {
 515  
 516          $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
 517  
 518          $machinename = 'TestLib';
 519          $majorversion = 1;
 520          $minorversion = 0;
 521          [$lib, $files] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 522              $minorversion);
 523  
 524          // Now run the API call.
 525          $this->h5p_file_storage->saveLibrary($lib);
 526  
 527          // Save a second library to ensure we aren't deleting all libraries, but just the one specified.
 528          $basedirectory = $this->h5p_tempath . '/' . 'awesomelib-2.1';
 529  
 530          $this->libraryid++;
 531          $machinename = 'AwesomeLib';
 532          $majorversion = 2;
 533          $minorversion = 1;
 534          [$lib2, $files2] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
 535              $minorversion);
 536  
 537          // Now run the API call.
 538          $this->h5p_file_storage->saveLibrary($lib2);
 539  
 540          $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
 541                  file_storage::LIBRARY_FILEAREA);
 542          $this->assertCount(14, $files);
 543  
 544          $this->h5p_file_storage->delete_library($lib);
 545  
 546          // Let's look at the records.
 547          $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
 548                  file_storage::LIBRARY_FILEAREA);
 549          $this->assertCount(7, $files);
 550  
 551          // Check that the db count is still the same after setting the libraryId to false.
 552          $lib['libraryId'] = false;
 553          $this->h5p_file_storage->delete_library($lib);
 554  
 555          $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
 556                  file_storage::LIBRARY_FILEAREA);
 557          $this->assertCount(7, $files);
 558      }
 559  
 560      /**
 561       * Test get_icon_url() function behaviour.
 562       *
 563       * @dataProvider get_icon_url_provider
 564       * @param  string  $filename  The name of the H5P file to load.
 565       * @param  bool    $expected  Whether the icon should exist or not.
 566       */
 567      public function test_get_icon_url(string $filename, bool $expected): void {
 568          global $DB;
 569  
 570          $this->resetAfterTest();
 571          $factory = new \core_h5p\factory();
 572  
 573          $admin = get_admin();
 574  
 575          // Prepare a valid .H5P file.
 576          $path = __DIR__ . '/fixtures/'.$filename;
 577  
 578          // Libraries can be updated when the file has been created by admin, even when the current user is not the admin.
 579          $this->setUser($admin);
 580          $file = helper::create_fake_stored_file_from_path($path, (int)$admin->id);
 581          $factory->get_framework()->set_file($file);
 582          $config = (object)[
 583              'frame' => 1,
 584              'export' => 1,
 585              'embed' => 0,
 586              'copyright' => 0,
 587          ];
 588  
 589          $h5pid = helper::save_h5p($factory, $file, $config);
 590          $h5p = $DB->get_record('h5p', ['id' => $h5pid]);
 591          $h5plib = $DB->get_record('h5p_libraries', ['id' => $h5p->mainlibraryid]);
 592          $iconurl = $this->h5p_file_storage->get_icon_url(
 593              $h5plib->id,
 594              $h5plib->machinename,
 595              $h5plib->majorversion,
 596              $h5plib->minorversion
 597          );
 598          if ($expected) {
 599              $this->assertContains(file_storage::ICON_FILENAME, $iconurl);
 600          } else {
 601              $this->assertFalse($iconurl);
 602          }
 603      }
 604  
 605      /**
 606       * Data provider for test_get_icon_url().
 607       *
 608       * @return array
 609       */
 610      public function get_icon_url_provider(): array {
 611          return [
 612              'Icon included' => [
 613                  'filltheblanks.h5p',
 614                  true,
 615              ],
 616              'Icon not included' => [
 617                  'greeting-card-887.h5p',
 618                  false,
 619              ],
 620          ];
 621      }
 622  
 623      /**
 624       * Test the private method get_file, a wrapper for getting an H5P content file.
 625       */
 626      public function test_get_file(): void {
 627  
 628          $this->setAdminUser();
 629          $file = 'img/fake.png';
 630          $h5pcontentid = 3;
 631  
 632          // Add a file to a H5P content.
 633          $this->h5p_generator->create_content_file($file, file_storage::CONTENT_FILEAREA, $h5pcontentid);
 634  
 635          // Set get_file method accessibility.
 636          $method = new ReflectionMethod(file_storage::class, 'get_file');
 637          $method->setAccessible(true);
 638  
 639          $contentfile = $method->invoke(new file_storage(), file_storage::CONTENT_FILEAREA, $h5pcontentid, $file);
 640  
 641          // Check that it returns an instance of store_file.
 642          $this->assertInstanceOf('stored_file', $contentfile);
 643  
 644          // Add a file to editor.
 645          $this->h5p_generator->create_content_file($file, 'draft', $h5pcontentid);
 646  
 647          $editorfile = $method->invoke(new file_storage(), 'draft', $h5pcontentid, $file);
 648  
 649          // Check that it returns an instance of store_file.
 650          $this->assertInstanceOf('stored_file', $editorfile);
 651      }
 652  
 653      /**
 654       * Test that a single file is added to Moodle files.
 655       */
 656      public function test_move_file(): void {
 657  
 658          // Create temp folder.
 659          $tempfolder = make_request_directory(false);
 660  
 661          // Create H5P content folder.
 662          $filepath = '/img/';
 663          $filename = 'fake.png';
 664          $h5pcontentfolder = $tempfolder . '/fakeH5Pcontent/content' . $filepath;
 665          if (!check_dir_exists($h5pcontentfolder, true, true)) {
 666              throw new moodle_exception('error_creating_temp_dir', 'error', $h5pcontentfolder);
 667          }
 668  
 669          $file = $h5pcontentfolder . $filename;
 670          touch($file);
 671  
 672          $h5pcontentid = 3;
 673  
 674          // Check the file doesn't exist in Moodle files.
 675          $this->assertFalse($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
 676              file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
 677  
 678          // Set get_file method accessibility.
 679          $method = new ReflectionMethod(file_storage::class, 'move_file');
 680          $method->setAccessible(true);
 681  
 682          $method->invoke(new file_storage(), $file, $h5pcontentid);
 683  
 684          // Check the file exist in Moodle files.
 685          $this->assertTrue($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
 686              file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
 687      }
 688  
 689      /**
 690       * Test that a file is copied from another H5P content or the H5P editor.
 691       *
 692       * @return void
 693       */
 694      public function test_cloneContentFile(): void {
 695  
 696          $admin = get_admin();
 697          $usercontext = \context_user::instance($admin->id);
 698          $this->setUser($admin);
 699          // Upload a file to the editor.
 700          $file = 'images/fake.jpg';
 701          $filepath = '/'.dirname($file).'/';
 702          $filename = basename($file);
 703  
 704          $content = 'abcd';
 705  
 706          $filerecord = array(
 707              'contextid' => $usercontext->id,
 708              'component' => 'user',
 709              'filearea'  => 'draft',
 710              'itemid'    => 0,
 711              'filepath'  => $filepath,
 712              'filename'  => $filename,
 713          );
 714  
 715          $this->h5p_fs_fs->create_file_from_string($filerecord, $content);
 716  
 717          // Target H5P content, where the file will be cloned.
 718          $targetcontent = new \stdClass();
 719          $targetcontent->id = 999;
 720  
 721          // Check the file doesn't exists before cloning.
 722          $this->assertFalse($this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
 723              file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
 724  
 725          // Copy file from the editor.
 726          $this->h5p_file_storage->cloneContentFile($file, 'editor', $targetcontent);
 727  
 728          // Check the file exists after cloning.
 729          $this->assertInstanceOf(\stored_file::class, $this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
 730              file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
 731  
 732          // Simulate that an H5P content, with id $sourcecontentid, has a file.
 733          $file = 'images/fake2.jpg';
 734          $filepath = '/'.dirname($file).'/';
 735          $filename = basename($file);
 736  
 737          $sourcecontentid = 111;
 738          $filerecord['contextid'] = $this->h5p_fs_context->id;
 739          $filerecord['component'] = file_storage::COMPONENT;
 740          $filerecord['filearea'] = file_storage::CONTENT_FILEAREA;
 741          $filerecord['itemid'] = $sourcecontentid;
 742          $filerecord['filepath'] = $filepath;
 743          $filerecord['filename'] = $filename;
 744  
 745          $this->h5p_fs_fs->create_file_from_string($filerecord, $content);
 746  
 747          // Check the file doesn't exists before cloning.
 748          $this->assertFalse($this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
 749              file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
 750  
 751          // Copy file from another H5P content.
 752          $this->h5p_file_storage->cloneContentFile($file, $sourcecontentid, $targetcontent);
 753  
 754          // Check the file exists after cloning.
 755          $this->assertInstanceOf(\stored_file::class, $this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
 756              file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
 757      }
 758  
 759      /**
 760       * Test that a given file exists in an H5P content.
 761       *
 762       * @return void
 763       */
 764      public function test_getContentFile(): void {
 765  
 766          $file = 'img/fake.png';
 767          $contentid = 3;
 768  
 769          // Add a file to a H5P content.
 770          $this->h5p_generator->create_content_file($file, file_storage::CONTENT_FILEAREA, $contentid);
 771  
 772          // Get an existing file id.
 773          $fileid = $this->h5p_file_storage->getContentFile($file, $contentid);
 774          $this->assertNotNull($fileid);
 775  
 776          // Try to get a nonexistent file.
 777          $fileid = $this->h5p_file_storage->getContentFile($file, 5);
 778          $this->assertNull($fileid);
 779      }
 780  
 781      /**
 782       * Tests that the content folder of an H5P content is imported in the Moodle filesystem.
 783       */
 784      public function test_moveContentDiretory(): void {
 785          global $DB;
 786  
 787          // Create temp folder.
 788          $tempfolder = make_request_directory(false);
 789  
 790          // Create H5P content folder.
 791          $h5pcontentfolder = $tempfolder . '/fakeH5Pcontent';
 792          $contentfolder = $h5pcontentfolder . '/content';
 793          if (!check_dir_exists($contentfolder, true, true)) {
 794              throw new moodle_exception('error_creating_temp_dir', 'error', $contentfolder);
 795          }
 796  
 797          // Add content.json file.
 798          touch($contentfolder . 'content.json');
 799  
 800          // Create several folders and files inside content folder.
 801          $filesexpected = array();
 802          $numfolders = random_int(2, 5);
 803          for ($numfolder = 1; $numfolder < $numfolders; $numfolder++) {
 804              $foldername = '/folder' . $numfolder;
 805              $newfolder = $contentfolder . $foldername;
 806              if (!check_dir_exists($newfolder, true, true)) {
 807                  throw new moodle_exception('error_creating_temp_dir', 'error', $newfolder);
 808              }
 809              $numfiles = random_int(2, 5);
 810              for ($numfile = 1; $numfile < $numfiles; $numfile++) {
 811                  $filename = '/file' . $numfile . '.ext';
 812                  touch($newfolder . $filename);
 813                  $filesexpected[] = $foldername . $filename;
 814              }
 815          }
 816  
 817          $targeth5pcontentid = 111;
 818          $this->h5p_file_storage->moveContentDirectory($h5pcontentfolder, $targeth5pcontentid);
 819  
 820          // Get database records.
 821          $sql = "SELECT concat(filepath, filename)
 822                    FROM {files}
 823                   WHERE filearea = :filearea AND itemid = :itemid AND component = :component AND filename != '.'";
 824          $params = [
 825              'component' => file_storage::COMPONENT,
 826              'filearea' => file_storage::CONTENT_FILEAREA,
 827              'itemid' => $targeth5pcontentid
 828          ];
 829          $filesdb = $DB->get_fieldset_sql($sql, $params);
 830          sort($filesdb);
 831  
 832          // Check that created files match with database records.
 833          $this->assertEquals($filesexpected, $filesdb);
 834      }
 835  
 836      /**
 837       * Test that an H5P content file is removed.
 838       */
 839      public function test_removeContentFile(): void {
 840  
 841          $file = 'img/fake.png';
 842          $filepath = '/' . dirname($file) . '/';
 843          $filename = basename($file);
 844          $h5pcontentid = 3;
 845  
 846          // Add a file to a H5P content.
 847          $this->h5p_generator->create_content_file($file, file_storage::CONTENT_FILEAREA, $h5pcontentid);
 848  
 849          // Check the file exists.
 850          $this->assertTrue($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
 851              file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
 852  
 853          $this->h5p_file_storage->removeContentFile($file, $h5pcontentid);
 854  
 855          // Check the file doesn't exists.
 856          $this->assertFalse($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
 857              file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
 858      }
 859  }