dc25c07f4afd9a3f6285c4b8070db27ee2ab7438
[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 file_archive;
30 use zip_archive;
32 defined('MOODLE_INTERNAL') || die();
34 /**
35  * Test class covering the H5PFileStorage interface implementation.
36  *
37  * @package    core_h5p
38  * @copyright  2019 Victor Deniz <victor@moodle.com>
39  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40  */
41 class h5p_file_storage_testcase extends \advanced_testcase {
43     /** @var \core_h5p\file_storage H5P file storage instance */
44     protected $h5p_file_storage;
45     /** @var \file_storage Core Moodle file_storage associated to the H5P file_storage */
46     protected $h5p_fs_fs;
47     /** @var \context Moodle context of the H5P file_storage */
48     protected $h5p_fs_context;
49     /** @var string Path to temp directory */
50     protected $h5p_tempath;
51     /** @var \core_h5p_generator  H5P generator instance */
52     protected $h5p_generator;
53     /** @var array $files an array used in the cache tests. */
54     protected $files = ['scripts' => [], 'styles' => []];
55     /** @var int $libraryid an id for the library. */
56     protected $libraryid = 1;
58     protected function setUp() {
59         parent::setUp();
60         $this->resetAfterTest(true);
62         // Fetch generator.
63         $generator = \testing_util::get_data_generator();
64         $this->h5p_generator = $generator->get_plugin_generator('core_h5p');
66         // Create file_storage_instance and create H5P temp directory.
67         $this->h5p_file_storage = new file_storage();
68         $this->h5p_tempath = $this->h5p_file_storage->getTmpPath();
69         check_dir_exists($this->h5p_tempath);
71         // Get value of protected properties.
72         $h5p_fs_rc = new \ReflectionClass(file_storage::class);
73         $h5p_file_storage_context = $h5p_fs_rc->getProperty('context');
74         $h5p_file_storage_context->setAccessible(true);
75         $this->h5p_fs_context = $h5p_file_storage_context->getValue($this->h5p_file_storage);
77         $h5p_file_storage_fs = $h5p_fs_rc->getProperty('fs');
78         $h5p_file_storage_fs->setAccessible(true);
79         $this->h5p_fs_fs = $h5p_file_storage_fs->getValue($this->h5p_file_storage);
80     }
82     /**
83      * Test that given the main directory of a library that all files are saved
84      * into the file system.
85      */
86     public function test_saveLibrary(): void {
88         $machinename = 'TestLib';
89         $majorversion = 1;
90         $minorversion = 0;
91         [$lib, $files] = $this->h5p_generator->create_library($this->h5p_tempath, $this->libraryid, $machinename, $majorversion,
92             $minorversion);
94         // Now run the API call.
95         $this->h5p_file_storage->saveLibrary($lib);
97         // Check that files are in the Moodle file system.
98         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
99             file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/", 'library.json');
100         $filepath = "/{$machinename}-{$majorversion}.{$minorversion}/";
101         $this->assertEquals($filepath, $file->get_filepath());
103         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
104             file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/scripts/", 'testlib.min.js');
105         $jsfilepath = "{$filepath}scripts/";
106         $this->assertEquals($jsfilepath, $file->get_filepath());
108         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
109             file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/styles/", 'testlib.min.css');
110         $cssfilepath = "{$filepath}styles/";
111         $this->assertEquals($cssfilepath, $file->get_filepath());
112     }
114     /**
115      * Test that a content file can be saved.
116      */
117     public function test_saveContent(): void {
119         $source = $this->h5p_tempath . '/' . 'content.json';
120         $this->h5p_generator->create_file($source);
122         $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
124         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
125             file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
126         $this->assertEquals(file_storage::CONTENT_FILEAREA, $file->get_filearea());
127         $this->assertEquals('content.json', $file->get_filename());
128         $this->assertEquals(5, $file->get_itemid());
129     }
131     /**
132      * Test that content files located on the file system can be deleted.
133      */
134     public function test_deleteContent(): void {
136         $source = $this->h5p_tempath . '/' . 'content.json';
137         $this->h5p_generator->create_file($source);
139         $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
141         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
142             file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
143         $this->assertEquals('content.json', $file->get_filename());
145         // Now to delete the record.
146         $this->h5p_file_storage->deleteContent(['id' => 5]);
147         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
148             file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
149         $this->assertFalse($file);
150     }
152     /**
153      * Test that returning a temp path returns what is expected by the h5p library.
154      */
155     public function test_getTmpPath(): void {
157         $temparray = explode('/', $this->h5p_tempath);
158         $h5pdirectory = array_pop($temparray);
159         $this->assertTrue(stripos($h5pdirectory, 'h5p-') === 0);
160     }
162     /**
163      * Test that the content files can be exported to a specified location.
164      */
165     public function test_exportContent(): void {
167         // Create a file to store.
168         $source = $this->h5p_tempath . '/' . 'content.json';
169         $this->h5p_generator->create_file($source);
171         $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
173         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
174             file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
175         $this->assertEquals('content.json', $file->get_filename());
177         // Now export it.
178         $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
179         check_dir_exists($destinationdirectory);
181         $this->h5p_file_storage->exportContent(5, $destinationdirectory);
182         // Check that there is a file now in that directory.
183         $contents = scandir($destinationdirectory);
184         $value = array_search('content.json', $contents);
185         $this->assertEquals('content.json', $contents[$value]);
186     }
188     /**
189      * Test that libraries on the file system can be exported to a specified location.
190      */
191     public function test_exportLibrary(): void {
193         $machinename = 'TestLib';
194         $majorversion = 1;
195         $minorversion = 0;
196         [$lib, $files] = $this->h5p_generator->create_library($this->h5p_tempath, $this->libraryid, $machinename, $majorversion,
197         $minorversion);
199         // Now run the API call.
200         $this->h5p_file_storage->saveLibrary($lib);
202         $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
203         check_dir_exists($destinationdirectory);
205         $this->h5p_file_storage->exportLibrary($lib, $destinationdirectory);
207         $filepath = "/{$machinename}-{$majorversion}.{$minorversion}/";
208         // There should be at least three items here (but could be more with . and ..).
209         $this->assertFileExists($destinationdirectory . $filepath . 'library.json');
210         $this->assertFileExists($destinationdirectory . $filepath . 'scripts/' . 'testlib.min.js');
211         $this->assertFileExists($destinationdirectory . $filepath . 'styles/' . 'testlib.min.css');
212     }
214     /**
215      * Test that an export file can be saved into the file system.
216      */
217     public function test_saveExport(): void {
219         $filename = 'someexportedfile.h5p';
220         $source = $this->h5p_tempath . '/' . $filename;
221         $this->h5p_generator->create_file($source);
223         $this->h5p_file_storage->saveExport($source, $filename);
225         // Check out if the file is there.
226         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
227             file_storage::EXPORT_FILEAREA, '0', '/', $filename);
228         $this->assertEquals(file_storage::EXPORT_FILEAREA, $file->get_filearea());
229     }
231     /**
232      * Test that an exort file can be deleted from the file system.
233      * @return [type] [description]
234      */
235     public function test_deleteExport(): void {
237         $filename = 'someexportedfile.h5p';
238         $source = $this->h5p_tempath . '/' . $filename;
239         $this->h5p_generator->create_file($source);
241         $this->h5p_file_storage->saveExport($source, $filename);
243         // Check out if the file is there.
244         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
245             file_storage::EXPORT_FILEAREA, '0', '/', $filename);
246         $this->assertEquals(file_storage::EXPORT_FILEAREA, $file->get_filearea());
248         // Time to delete.
249         $this->h5p_file_storage->deleteExport($filename);
251         // Check out if the file is there.
252         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
253             file_storage::EXPORT_FILEAREA, '0', '/', $filename);
254         $this->assertFalse($file);
255     }
257     /**
258      * Test to check if an export file already exists on the file system.
259      */
260     public function test_hasExport(): void {
262         $filename = 'someexportedfile.h5p';
263         $source = $this->h5p_tempath . '/' . $filename;
264         $this->h5p_generator->create_file($source);
266         // Check that it doesn't exist in the file system.
267         $this->assertFalse($this->h5p_file_storage->hasExport($filename));
269         $this->h5p_file_storage->saveExport($source, $filename);
270         // Now it should be present.
271         $this->assertTrue($this->h5p_file_storage->hasExport($filename));
272     }
274     /**
275      * Test that all the library files for an H5P activity can be concatenated into "cache" files. One for js and another for css.
276      */
277     public function test_cacheAssets(): void {
279         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
281         $machinename = 'TestLib';
282         $majorversion = 1;
283         $minorversion = 0;
284         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
285             $minorversion);
286         array_push($this->files['scripts'], ...$libfiles['scripts']);
287         array_push($this->files['styles'], ...$libfiles['styles']);
289         // Now run the API call.
290         $this->h5p_file_storage->saveLibrary($lib);
292         // Second library.
293         $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
295         $this->libraryid++;
296         $machinename = 'SuperTest';
297         $majorversion = 2;
298         $minorversion = 4;
299         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
300             $minorversion);
301         array_push($this->files['scripts'], ...$libfiles['scripts']);
302         array_push($this->files['styles'], ...$libfiles['styles']);
304         $this->h5p_file_storage->saveLibrary($lib);
306         $this->assertCount(2, $this->files['scripts']);
307         $this->assertCount(2, $this->files['styles']);
309         $key = 'testhashkey';
311         $this->h5p_file_storage->cacheAssets($this->files, $key);
312         $this->assertCount(1, $this->files['scripts']);
313         $this->assertCount(1, $this->files['styles']);
316         $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.js';
317         $this->assertEquals($expectedfile, $this->files['scripts'][0]->path);
318         $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.css';
319         $this->assertEquals($expectedfile, $this->files['styles'][0]->path);
320     }
322     /**
323      * Test that cached files can be retrieved via a key.
324      */
325     public function test_getCachedAssets() {
327         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
329         $machinename = 'TestLib';
330         $majorversion = 1;
331         $minorversion = 0;
332         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
333             $minorversion);
334         array_push($this->files['scripts'], ...$libfiles['scripts']);
335         array_push($this->files['styles'], ...$libfiles['styles']);
337         // Now run the API call.
338         $this->h5p_file_storage->saveLibrary($lib);
340         // Second library.
341         $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
343         $this->libraryid++;
344         $machinename = 'SuperTest';
345         $majorversion = 2;
346         $minorversion = 4;
347         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
348             $minorversion);
349         array_push($this->files['scripts'], ...$libfiles['scripts']);
350         array_push($this->files['styles'], ...$libfiles['styles']);
352         $this->h5p_file_storage->saveLibrary($lib);
354         $this->assertCount(2, $this->files['scripts']);
355         $this->assertCount(2, $this->files['styles']);
357         $key = 'testhashkey';
359         $this->h5p_file_storage->cacheAssets($this->files, $key);
361         $testarray = $this->h5p_file_storage->getCachedAssets($key);
362         $this->assertCount(1, $testarray['scripts']);
363         $this->assertCount(1, $testarray['styles']);
364         $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.js';
365         $this->assertEquals($expectedfile, $testarray['scripts'][0]->path);
366         $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.css';
367         $this->assertEquals($expectedfile, $testarray['styles'][0]->path);
368     }
370     /**
371      * Test that cache files in the files system can be removed.
372      */
373     public function test_deleteCachedAssets(): void {
374         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
376         $machinename = 'TestLib';
377         $majorversion = 1;
378         $minorversion = 0;
379         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
380             $minorversion);
381         array_push($this->files['scripts'], ...$libfiles['scripts']);
382         array_push($this->files['styles'], ...$libfiles['styles']);
384         // Now run the API call.
385         $this->h5p_file_storage->saveLibrary($lib);
387         // Second library.
388         $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
390         $this->libraryid++;
391         $machinename = 'SuperTest';
392         $majorversion = 2;
393         $minorversion = 4;
394         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
395             $minorversion);
396         array_push($this->files['scripts'], ...$libfiles['scripts']);
397         array_push($this->files['styles'], ...$libfiles['styles']);
399         $this->h5p_file_storage->saveLibrary($lib);
401         $this->assertCount(2, $this->files['scripts']);
402         $this->assertCount(2, $this->files['styles']);
404         $key = 'testhashkey';
406         $this->h5p_file_storage->cacheAssets($this->files, $key);
408         $testarray = $this->h5p_file_storage->getCachedAssets($key);
409         $this->assertCount(1, $testarray['scripts']);
410         $this->assertCount(1, $testarray['styles']);
412         // Time to delete.
413         $this->h5p_file_storage->deleteCachedAssets([$key]);
414         $testarray = $this->h5p_file_storage->getCachedAssets($key);
415         $this->assertNull($testarray);
416     }
418     /**
419      * Retrieve content from a file given a specific path.
420      */
421     public function test_getContent() {
422         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
424         $machinename = 'TestLib';
425         $majorversion = 1;
426         $minorversion = 0;
427         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
428             $minorversion);
429         array_push($this->files['scripts'], ...$libfiles['scripts']);
430         array_push($this->files['styles'], ...$libfiles['styles']);
432         // Now run the API call.
433         $this->h5p_file_storage->saveLibrary($lib);
435         $content = $this->h5p_file_storage->getContent($this->files['scripts'][0]->path);
436         // The file content is created based on the file system path (\core_h5p_generator::create_file).
437         $expectedcontent = hash("md5", $basedirectory. '/' . 'scripts' . '/' . 'testlib.min.js');
439         $this->assertEquals($expectedcontent, $content);
440     }
442     /**
443      * Test that an upgrade script can be found on the file system.
444      */
445     public function test_getUpgradeScript() {
446         // Upload an upgrade file.
447         $machinename = 'TestLib';
448         $majorversion = 3;
449         $minorversion = 1;
450         $filepath = '/' . "{$machinename}-{$majorversion}.{$minorversion}" . '/';
451         $fs = get_file_storage();
452         $filerecord = [
453             'contextid' => \context_system::instance()->id,
454             'component' => file_storage::COMPONENT,
455             'filearea' => file_storage::LIBRARY_FILEAREA,
456             'itemid' => 15,
457             'filepath' => $filepath,
458             'filename' => 'upgrade.js'
459         ];
460         $filestorage = new file_storage();
461         $fs->create_file_from_string($filerecord, 'test string info');
462         $expectedfilepath = DIRECTORY_SEPARATOR . file_storage::LIBRARY_FILEAREA . $filepath . 'upgrade.js';
463         $this->assertEquals($expectedfilepath, $filestorage->getUpgradeScript($machinename, $majorversion, $minorversion));
464         $this->assertNull($filestorage->getUpgradeScript($machinename, $majorversion, 7));
465     }
467     /**
468      * Test that information from a source can be saved to the specified path.
469      * The zip file has the following contents
470      * - h5ptest
471      * |- content
472      * |     |- content.json
473      * |- testFont
474      * |     |- testfont.min.css
475      * |- testJavaScript
476      * |     |- testscript.min.js
477      * |- h5p.json
478      */
479     public function test_saveFileFromZip() {
481         $ziparchive = new zip_archive();
482         $path = __DIR__ . '/fixtures/h5ptest.zip';
483         $result = $ziparchive->open($path, file_archive::OPEN);
485         $files = $ziparchive->list_files();
486         foreach ($files as $file) {
487             if (!$file->is_directory) {
488                 $stream = $ziparchive->get_stream($file->index);
489                 $items = explode(DIRECTORY_SEPARATOR, $file->pathname);
490                 array_shift($items);
491                 $path = implode(DIRECTORY_SEPARATOR, $items);
492                 $this->h5p_file_storage->saveFileFromZip($this->h5p_tempath, $path, $stream);
493                 $filestocheck[] = $path;
494             }
495         }
496         $ziparchive->close();
498         foreach ($filestocheck as $filetocheck) {
499             $pathtocheck = $this->h5p_tempath .'/'. $filetocheck;
500             $this->assertFileExists($pathtocheck);
501         }
502     }
504     /**
505      * Test that a library is fully deleted from the file system
506      */
507     public function test_delete_library() {
509         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
511         $machinename = 'TestLib';
512         $majorversion = 1;
513         $minorversion = 0;
514         [$lib, $files] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
515             $minorversion);
517         // Now run the API call.
518         $this->h5p_file_storage->saveLibrary($lib);
520         // Save a second library to ensure we aren't deleting all libraries, but just the one specified.
521         $basedirectory = $this->h5p_tempath . '/' . 'awesomelib-2.1';
523         $this->libraryid++;
524         $machinename = 'AwesomeLib';
525         $majorversion = 2;
526         $minorversion = 1;
527         [$lib2, $files2] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
528             $minorversion);
530         // Now run the API call.
531         $this->h5p_file_storage->saveLibrary($lib2);
533         $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
534                 file_storage::LIBRARY_FILEAREA);
535         $this->assertCount(14, $files);
537         $this->h5p_file_storage->delete_library($lib);
539         // Let's look at the records.
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(7, $files);
544         // Check that the db count is still the same after setting the libraryId to false.
545         $lib['libraryId'] = false;
546         $this->h5p_file_storage->delete_library($lib);
548         $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
549                 file_storage::LIBRARY_FILEAREA);
550         $this->assertCount(7, $files);
551     }