d94d395831b3f9c57791dd4ff0e1252ab8898d25
[moodle.git] / h5p / tests / h5p_file_storage_test.php
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/>.
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  */
26 namespace core_h5p\local\tests;
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;
36 defined('MOODLE_INTERNAL') || die();
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 {
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;
63     protected function setUp() {
64         parent::setUp();
65         $this->resetAfterTest(true);
67         autoloader::register();
69         // Fetch generator.
70         $generator = \testing_util::get_data_generator();
71         $this->h5p_generator = $generator->get_plugin_generator('core_h5p');
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);
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);
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     }
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 {
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);
101         // Now run the API call.
102         $this->h5p_file_storage->saveLibrary($lib);
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());
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());
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     }
121     /**
122      * Test that a content file can be saved.
123      */
124     public function test_saveContent(): void {
126         $source = $this->h5p_tempath . '/' . 'content.json';
127         $this->h5p_generator->create_file($source);
129         $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
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     }
138     /**
139      * Test that content files located on the file system can be deleted.
140      */
141     public function test_deleteContent(): void {
143         $source = $this->h5p_tempath . '/' . 'content.json';
144         $this->h5p_generator->create_file($source);
146         $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
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());
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     }
159     /**
160      * Test that returning a temp path returns what is expected by the h5p library.
161      */
162     public function test_getTmpPath(): void {
164         $temparray = explode('/', $this->h5p_tempath);
165         $h5pdirectory = array_pop($temparray);
166         $this->assertTrue(stripos($h5pdirectory, 'h5p-') === 0);
167     }
169     /**
170      * Test that the content files can be exported to a specified location.
171      */
172     public function test_exportContent(): void {
174         // Create a file to store.
175         $source = $this->h5p_tempath . '/' . 'content.json';
176         $this->h5p_generator->create_file($source);
178         $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
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());
184         // Now export it.
185         $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
186         check_dir_exists($destinationdirectory);
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     }
195     /**
196      * Test that libraries on the file system can be exported to a specified location.
197      */
198     public function test_exportLibrary(): void {
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);
206         // Now run the API call.
207         $this->h5p_file_storage->saveLibrary($lib);
209         $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
210         check_dir_exists($destinationdirectory);
212         $this->h5p_file_storage->exportLibrary($lib, $destinationdirectory);
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     }
221     /**
222      * Test that an export file can be saved into the file system.
223      */
224     public function test_saveExport(): void {
226         $filename = 'someexportedfile.h5p';
227         $source = $this->h5p_tempath . '/' . $filename;
228         $this->h5p_generator->create_file($source);
230         $this->h5p_file_storage->saveExport($source, $filename);
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     }
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 {
244         $filename = 'someexportedfile.h5p';
245         $source = $this->h5p_tempath . '/' . $filename;
246         $this->h5p_generator->create_file($source);
248         $this->h5p_file_storage->saveExport($source, $filename);
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());
255         // Time to delete.
256         $this->h5p_file_storage->deleteExport($filename);
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     }
264     /**
265      * Test to check if an export file already exists on the file system.
266      */
267     public function test_hasExport(): void {
269         $filename = 'someexportedfile.h5p';
270         $source = $this->h5p_tempath . '/' . $filename;
271         $this->h5p_generator->create_file($source);
273         // Check that it doesn't exist in the file system.
274         $this->assertFalse($this->h5p_file_storage->hasExport($filename));
276         $this->h5p_file_storage->saveExport($source, $filename);
277         // Now it should be present.
278         $this->assertTrue($this->h5p_file_storage->hasExport($filename));
279     }
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 {
286         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
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']);
296         // Now run the API call.
297         $this->h5p_file_storage->saveLibrary($lib);
299         // Second library.
300         $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
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']);
311         $this->h5p_file_storage->saveLibrary($lib);
313         $this->assertCount(2, $this->files['scripts']);
314         $this->assertCount(2, $this->files['styles']);
316         $key = 'testhashkey';
318         $this->h5p_file_storage->cacheAssets($this->files, $key);
319         $this->assertCount(1, $this->files['scripts']);
320         $this->assertCount(1, $this->files['styles']);
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     }
329     /**
330      * Test that cached files can be retrieved via a key.
331      */
332     public function test_getCachedAssets() {
334         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
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']);
344         // Now run the API call.
345         $this->h5p_file_storage->saveLibrary($lib);
347         // Second library.
348         $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
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']);
359         $this->h5p_file_storage->saveLibrary($lib);
361         $this->assertCount(2, $this->files['scripts']);
362         $this->assertCount(2, $this->files['styles']);
364         $key = 'testhashkey';
366         $this->h5p_file_storage->cacheAssets($this->files, $key);
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     }
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';
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']);
391         // Now run the API call.
392         $this->h5p_file_storage->saveLibrary($lib);
394         // Second library.
395         $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
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']);
406         $this->h5p_file_storage->saveLibrary($lib);
408         $this->assertCount(2, $this->files['scripts']);
409         $this->assertCount(2, $this->files['styles']);
411         $key = 'testhashkey';
413         $this->h5p_file_storage->cacheAssets($this->files, $key);
415         $testarray = $this->h5p_file_storage->getCachedAssets($key);
416         $this->assertCount(1, $testarray['scripts']);
417         $this->assertCount(1, $testarray['styles']);
419         // Time to delete.
420         $this->h5p_file_storage->deleteCachedAssets([$key]);
421         $testarray = $this->h5p_file_storage->getCachedAssets($key);
422         $this->assertNull($testarray);
423     }
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';
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']);
439         // Now run the API call.
440         $this->h5p_file_storage->saveLibrary($lib);
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');
446         $this->assertEquals($expectedcontent, $content);
447     }
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     }
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() {
488         $ziparchive = new zip_archive();
489         $path = __DIR__ . '/fixtures/h5ptest.zip';
490         $result = $ziparchive->open($path, file_archive::OPEN);
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();
505         foreach ($filestocheck as $filetocheck) {
506             $pathtocheck = $this->h5p_tempath .'/'. $filetocheck;
507             $this->assertFileExists($pathtocheck);
508         }
509     }
511     /**
512      * Test that a library is fully deleted from the file system
513      */
514     public function test_delete_library() {
516         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
518         $machinename = 'TestLib';
519         $majorversion = 1;
520         $minorversion = 0;
521         [$lib, $files] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
522             $minorversion);
524         // Now run the API call.
525         $this->h5p_file_storage->saveLibrary($lib);
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';
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);
537         // Now run the API call.
538         $this->h5p_file_storage->saveLibrary($lib2);
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);
544         $this->h5p_file_storage->delete_library($lib);
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);
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);
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     }
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;
570         $this->resetAfterTest();
571         $factory = new \core_h5p\factory();
573         $admin = get_admin();
575         // Prepare a valid .H5P file.
576         $path = __DIR__ . '/fixtures/'.$filename;
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         ];
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     }
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     }
623     /**
624      * Test the private method get_file, a wrapper for getting an H5P content file.
625      */
626     public function test_get_file(): void {
628         $file = 'img/fake.png';
629         $h5pcontentid = 3;
631         // Add a file to a H5P content.
632         $this->h5p_generator->create_content_file($file, file_storage::CONTENT_FILEAREA, $h5pcontentid);
634         // Set get_file method accessibility.
635         $method = new ReflectionMethod(file_storage::class, 'get_file');
636         $method->setAccessible(true);
638         $contentfile = $method->invoke(new file_storage(), file_storage::CONTENT_FILEAREA, $h5pcontentid, $file);
640         // Check that it returns an instance of store_file.
641         $this->assertInstanceOf('stored_file', $contentfile);
643         // Add a file to editor.
644         $this->h5p_generator->create_content_file($file, file_storage::EDITOR_FILEAREA, $h5pcontentid);
646         $editorfile = $method->invoke(new file_storage(), file_storage::EDITOR_FILEAREA, $h5pcontentid, $file);
648         // Check that it returns an instance of store_file.
649         $this->assertInstanceOf('stored_file', $editorfile);
650     }
652     /**
653      * Test that a single file is added to Moodle files.
654      */
655     public function test_move_file(): void {
657         // Create temp folder.
658         $tempfolder = make_request_directory(false);
660         // Create H5P content folder.
661         $filepath = '/img/';
662         $filename = 'fake.png';
663         $h5pcontentfolder = $tempfolder . '/fakeH5Pcontent/content' . $filepath;
664         if (!check_dir_exists($h5pcontentfolder, true, true)) {
665             throw new moodle_exception('error_creating_temp_dir', 'error', $h5pcontentfolder);
666         }
668         $file = $h5pcontentfolder . $filename;
669         touch($file);
671         $h5pcontentid = 3;
673         // Check the file doesn't exist in Moodle files.
674         $this->assertFalse($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
675             file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
677         // Set get_file method accessibility.
678         $method = new ReflectionMethod(file_storage::class, 'move_file');
679         $method->setAccessible(true);
681         $method->invoke(new file_storage(), $file, $h5pcontentid);
683         // Check the file exist in Moodle files.
684         $this->assertTrue($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
685             file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
686     }
688     /**
689      * Test that a file is copied from another H5P content or the H5P editor.
690      *
691      * @return void
692      */
693     public function test_cloneContentFile(): void {
695         // Upload a file to the editor.
696         $file = 'images/fake.jpg';
697         $filepath = '/'.dirname($file).'/';
698         $filename = basename($file);
700         $content = 'abcd';
702         $filerecord = array(
703             'contextid' => $this->h5p_fs_context->id,
704             'component' => file_storage::COMPONENT,
705             'filearea'  => file_storage::EDITOR_FILEAREA,
706             'itemid'    => 0,
707             'filepath'  => $filepath,
708             'filename'  => $filename,
709         );
711         $this->h5p_fs_fs->create_file_from_string($filerecord, $content);
713         // Target H5P content, where the file will be cloned.
714         $targetcontent = new \stdClass();
715         $targetcontent->id = 999;
717         // Check the file doesn't exists before cloning.
718         $this->assertFalse($this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
719             file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
721         // Copy file from the editor.
722         $this->h5p_file_storage->cloneContentFile($file, 'editor', $targetcontent);
724         // Check the file exists after cloning.
725         $this->assertInstanceOf(\stored_file::class, $this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
726             file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
728         // Simulate that an H5P content, with id $sourcecontentid, has a file.
729         $file = 'images/fake2.jpg';
730         $filepath = '/'.dirname($file).'/';
731         $filename = basename($file);
733         $sourcecontentid = 111;
734         $filerecord['filearea'] = 'content';
735         $filerecord['itemid'] = $sourcecontentid;
736         $filerecord['filepath'] = $filepath;
737         $filerecord['filename'] = $filename;
739         $this->h5p_fs_fs->create_file_from_string($filerecord, $content);
741         // Check the file doesn't exists before cloning.
742         $this->assertFalse($this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
743             file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
745         // Copy file from another H5P content.
746         $this->h5p_file_storage->cloneContentFile($file, $sourcecontentid, $targetcontent);
748         // Check the file exists after cloning.
749         $this->assertInstanceOf(\stored_file::class, $this->h5p_fs_fs->get_file($this->h5p_fs_context->id, file_storage::COMPONENT,
750             file_storage::CONTENT_FILEAREA, $targetcontent->id, $filepath, $filename));
751     }
753     /**
754      * Test that a given file exists in an H5P content.
755      *
756      * @return void
757      */
758     public function test_getContentFile(): void {
760         $file = 'img/fake.png';
761         $contentid = 3;
763         // Add a file to a H5P content.
764         $this->h5p_generator->create_content_file($file, file_storage::CONTENT_FILEAREA, $contentid);
766         // Get an existing file id.
767         $fileid = $this->h5p_file_storage->getContentFile($file, $contentid);
768         $this->assertNotNull($fileid);
770         // Try to get a nonexistent file.
771         $fileid = $this->h5p_file_storage->getContentFile($file, 5);
772         $this->assertNull($fileid);
773     }
775     /**
776      * Tests that the content folder of an H5P content is imported in the Moodle filesystem.
777      */
778     public function test_moveContentDiretory(): void {
779         global $DB;
781         // Create temp folder.
782         $tempfolder = make_request_directory(false);
784         // Create H5P content folder.
785         $h5pcontentfolder = $tempfolder . '/fakeH5Pcontent';
786         $contentfolder = $h5pcontentfolder . '/content';
787         if (!check_dir_exists($contentfolder, true, true)) {
788             throw new moodle_exception('error_creating_temp_dir', 'error', $contentfolder);
789         }
791         // Add content.json file.
792         touch($contentfolder . 'content.json');
794         // Create several folders and files inside content folder.
795         $filesexpected = array();
796         $numfolders = random_int(2, 5);
797         for ($numfolder = 1; $numfolder < $numfolders; $numfolder++) {
798             $foldername = '/folder' . $numfolder;
799             $newfolder = $contentfolder . $foldername;
800             if (!check_dir_exists($newfolder, true, true)) {
801                 throw new moodle_exception('error_creating_temp_dir', 'error', $newfolder);
802             }
803             $numfiles = random_int(2, 5);
804             for ($numfile = 1; $numfile < $numfiles; $numfile++) {
805                 $filename = '/file' . $numfile . '.ext';
806                 touch($newfolder . $filename);
807                 $filesexpected[] = $foldername . $filename;
808             }
809         }
811         $targeth5pcontentid = 111;
812         $this->h5p_file_storage->moveContentDirectory($h5pcontentfolder, $targeth5pcontentid);
814         // Get database records.
815         $sql = "SELECT concat(filepath, filename)
816                   FROM {files}
817                  WHERE filearea = :filearea AND itemid = :itemid AND component = :component AND filename != '.'";
818         $params = [
819             'component' => file_storage::COMPONENT,
820             'filearea' => file_storage::CONTENT_FILEAREA,
821             'itemid' => $targeth5pcontentid
822         ];
823         $filesdb = $DB->get_fieldset_sql($sql, $params);
824         sort($filesdb);
826         // Check that created files match with database records.
827         $this->assertEquals($filesexpected, $filesdb);
828     }
830     /**
831      * Test that an H5P content file is removed.
832      */
833     public function test_removeContentFile(): void {
835         $file = 'img/fake.png';
836         $filepath = '/' . dirname($file) . '/';
837         $filename = basename($file);
838         $h5pcontentid = 3;
840         // Add a file to a H5P content.
841         $this->h5p_generator->create_content_file($file, file_storage::CONTENT_FILEAREA, $h5pcontentid);
843         // Check the file exists.
844         $this->assertTrue($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
845             file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
847         $this->h5p_file_storage->removeContentFile($file, $h5pcontentid);
849         // Check the file doesn't exists.
850         $this->assertFalse($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
851             file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
852     }