MDL-67100 core_h5p: Add autoloader to the h5p_file_storage_test
[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\autoloader;
30 use file_archive;
31 use zip_archive;
33 defined('MOODLE_INTERNAL') || die();
35 /**
36  * Test class covering the H5PFileStorage interface implementation.
37  *
38  * @package    core_h5p
39  * @copyright  2019 Victor Deniz <victor@moodle.com>
40  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  */
42 class h5p_file_storage_testcase extends \advanced_testcase {
44     /** @var \core_h5p\file_storage H5P file storage instance */
45     protected $h5p_file_storage;
46     /** @var \file_storage Core Moodle file_storage associated to the H5P file_storage */
47     protected $h5p_fs_fs;
48     /** @var \context Moodle context of the H5P file_storage */
49     protected $h5p_fs_context;
50     /** @var string Path to temp directory */
51     protected $h5p_tempath;
52     /** @var \core_h5p_generator  H5P generator instance */
53     protected $h5p_generator;
54     /** @var array $files an array used in the cache tests. */
55     protected $files = ['scripts' => [], 'styles' => []];
56     /** @var int $libraryid an id for the library. */
57     protected $libraryid = 1;
59     protected function setUp() {
60         parent::setUp();
61         $this->resetAfterTest(true);
63         autoloader::register();
65         // Fetch generator.
66         $generator = \testing_util::get_data_generator();
67         $this->h5p_generator = $generator->get_plugin_generator('core_h5p');
69         // Create file_storage_instance and create H5P temp directory.
70         $this->h5p_file_storage = new file_storage();
71         $this->h5p_tempath = $this->h5p_file_storage->getTmpPath();
72         check_dir_exists($this->h5p_tempath);
74         // Get value of protected properties.
75         $h5p_fs_rc = new \ReflectionClass(file_storage::class);
76         $h5p_file_storage_context = $h5p_fs_rc->getProperty('context');
77         $h5p_file_storage_context->setAccessible(true);
78         $this->h5p_fs_context = $h5p_file_storage_context->getValue($this->h5p_file_storage);
80         $h5p_file_storage_fs = $h5p_fs_rc->getProperty('fs');
81         $h5p_file_storage_fs->setAccessible(true);
82         $this->h5p_fs_fs = $h5p_file_storage_fs->getValue($this->h5p_file_storage);
83     }
85     /**
86      * Test that given the main directory of a library that all files are saved
87      * into the file system.
88      */
89     public function test_saveLibrary(): void {
91         $machinename = 'TestLib';
92         $majorversion = 1;
93         $minorversion = 0;
94         [$lib, $files] = $this->h5p_generator->create_library($this->h5p_tempath, $this->libraryid, $machinename, $majorversion,
95             $minorversion);
97         // Now run the API call.
98         $this->h5p_file_storage->saveLibrary($lib);
100         // Check that files are in the Moodle file system.
101         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
102             file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/", 'library.json');
103         $filepath = "/{$machinename}-{$majorversion}.{$minorversion}/";
104         $this->assertEquals($filepath, $file->get_filepath());
106         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
107             file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/scripts/", 'testlib.min.js');
108         $jsfilepath = "{$filepath}scripts/";
109         $this->assertEquals($jsfilepath, $file->get_filepath());
111         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
112             file_storage::LIBRARY_FILEAREA, '1', "/{$machinename}-{$majorversion}.{$minorversion}/styles/", 'testlib.min.css');
113         $cssfilepath = "{$filepath}styles/";
114         $this->assertEquals($cssfilepath, $file->get_filepath());
115     }
117     /**
118      * Test that a content file can be saved.
119      */
120     public function test_saveContent(): void {
122         $source = $this->h5p_tempath . '/' . 'content.json';
123         $this->h5p_generator->create_file($source);
125         $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
127         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
128             file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
129         $this->assertEquals(file_storage::CONTENT_FILEAREA, $file->get_filearea());
130         $this->assertEquals('content.json', $file->get_filename());
131         $this->assertEquals(5, $file->get_itemid());
132     }
134     /**
135      * Test that content files located on the file system can be deleted.
136      */
137     public function test_deleteContent(): void {
139         $source = $this->h5p_tempath . '/' . 'content.json';
140         $this->h5p_generator->create_file($source);
142         $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
144         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
145             file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
146         $this->assertEquals('content.json', $file->get_filename());
148         // Now to delete the record.
149         $this->h5p_file_storage->deleteContent(['id' => 5]);
150         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
151             file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
152         $this->assertFalse($file);
153     }
155     /**
156      * Test that returning a temp path returns what is expected by the h5p library.
157      */
158     public function test_getTmpPath(): void {
160         $temparray = explode('/', $this->h5p_tempath);
161         $h5pdirectory = array_pop($temparray);
162         $this->assertTrue(stripos($h5pdirectory, 'h5p-') === 0);
163     }
165     /**
166      * Test that the content files can be exported to a specified location.
167      */
168     public function test_exportContent(): void {
170         // Create a file to store.
171         $source = $this->h5p_tempath . '/' . 'content.json';
172         $this->h5p_generator->create_file($source);
174         $this->h5p_file_storage->saveContent($this->h5p_tempath, ['id' => 5]);
176         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
177             file_storage::CONTENT_FILEAREA, '5', '/', 'content.json');
178         $this->assertEquals('content.json', $file->get_filename());
180         // Now export it.
181         $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
182         check_dir_exists($destinationdirectory);
184         $this->h5p_file_storage->exportContent(5, $destinationdirectory);
185         // Check that there is a file now in that directory.
186         $contents = scandir($destinationdirectory);
187         $value = array_search('content.json', $contents);
188         $this->assertEquals('content.json', $contents[$value]);
189     }
191     /**
192      * Test that libraries on the file system can be exported to a specified location.
193      */
194     public function test_exportLibrary(): void {
196         $machinename = 'TestLib';
197         $majorversion = 1;
198         $minorversion = 0;
199         [$lib, $files] = $this->h5p_generator->create_library($this->h5p_tempath, $this->libraryid, $machinename, $majorversion,
200         $minorversion);
202         // Now run the API call.
203         $this->h5p_file_storage->saveLibrary($lib);
205         $destinationdirectory = $this->h5p_tempath . '/' . 'testdir';
206         check_dir_exists($destinationdirectory);
208         $this->h5p_file_storage->exportLibrary($lib, $destinationdirectory);
210         $filepath = "/{$machinename}-{$majorversion}.{$minorversion}/";
211         // There should be at least three items here (but could be more with . and ..).
212         $this->assertFileExists($destinationdirectory . $filepath . 'library.json');
213         $this->assertFileExists($destinationdirectory . $filepath . 'scripts/' . 'testlib.min.js');
214         $this->assertFileExists($destinationdirectory . $filepath . 'styles/' . 'testlib.min.css');
215     }
217     /**
218      * Test that an export file can be saved into the file system.
219      */
220     public function test_saveExport(): void {
222         $filename = 'someexportedfile.h5p';
223         $source = $this->h5p_tempath . '/' . $filename;
224         $this->h5p_generator->create_file($source);
226         $this->h5p_file_storage->saveExport($source, $filename);
228         // Check out if the file is there.
229         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
230             file_storage::EXPORT_FILEAREA, '0', '/', $filename);
231         $this->assertEquals(file_storage::EXPORT_FILEAREA, $file->get_filearea());
232     }
234     /**
235      * Test that an exort file can be deleted from the file system.
236      * @return [type] [description]
237      */
238     public function test_deleteExport(): void {
240         $filename = 'someexportedfile.h5p';
241         $source = $this->h5p_tempath . '/' . $filename;
242         $this->h5p_generator->create_file($source);
244         $this->h5p_file_storage->saveExport($source, $filename);
246         // Check out if the file is there.
247         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
248             file_storage::EXPORT_FILEAREA, '0', '/', $filename);
249         $this->assertEquals(file_storage::EXPORT_FILEAREA, $file->get_filearea());
251         // Time to delete.
252         $this->h5p_file_storage->deleteExport($filename);
254         // Check out if the file is there.
255         $file =  $this->h5p_fs_fs->get_file ($this->h5p_fs_context->id, file_storage::COMPONENT,
256             file_storage::EXPORT_FILEAREA, '0', '/', $filename);
257         $this->assertFalse($file);
258     }
260     /**
261      * Test to check if an export file already exists on the file system.
262      */
263     public function test_hasExport(): void {
265         $filename = 'someexportedfile.h5p';
266         $source = $this->h5p_tempath . '/' . $filename;
267         $this->h5p_generator->create_file($source);
269         // Check that it doesn't exist in the file system.
270         $this->assertFalse($this->h5p_file_storage->hasExport($filename));
272         $this->h5p_file_storage->saveExport($source, $filename);
273         // Now it should be present.
274         $this->assertTrue($this->h5p_file_storage->hasExport($filename));
275     }
277     /**
278      * Test that all the library files for an H5P activity can be concatenated into "cache" files. One for js and another for css.
279      */
280     public function test_cacheAssets(): void {
282         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
284         $machinename = 'TestLib';
285         $majorversion = 1;
286         $minorversion = 0;
287         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
288             $minorversion);
289         array_push($this->files['scripts'], ...$libfiles['scripts']);
290         array_push($this->files['styles'], ...$libfiles['styles']);
292         // Now run the API call.
293         $this->h5p_file_storage->saveLibrary($lib);
295         // Second library.
296         $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
298         $this->libraryid++;
299         $machinename = 'SuperTest';
300         $majorversion = 2;
301         $minorversion = 4;
302         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
303             $minorversion);
304         array_push($this->files['scripts'], ...$libfiles['scripts']);
305         array_push($this->files['styles'], ...$libfiles['styles']);
307         $this->h5p_file_storage->saveLibrary($lib);
309         $this->assertCount(2, $this->files['scripts']);
310         $this->assertCount(2, $this->files['styles']);
312         $key = 'testhashkey';
314         $this->h5p_file_storage->cacheAssets($this->files, $key);
315         $this->assertCount(1, $this->files['scripts']);
316         $this->assertCount(1, $this->files['styles']);
319         $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.js';
320         $this->assertEquals($expectedfile, $this->files['scripts'][0]->path);
321         $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.css';
322         $this->assertEquals($expectedfile, $this->files['styles'][0]->path);
323     }
325     /**
326      * Test that cached files can be retrieved via a key.
327      */
328     public function test_getCachedAssets() {
330         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
332         $machinename = 'TestLib';
333         $majorversion = 1;
334         $minorversion = 0;
335         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
336             $minorversion);
337         array_push($this->files['scripts'], ...$libfiles['scripts']);
338         array_push($this->files['styles'], ...$libfiles['styles']);
340         // Now run the API call.
341         $this->h5p_file_storage->saveLibrary($lib);
343         // Second library.
344         $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
346         $this->libraryid++;
347         $machinename = 'SuperTest';
348         $majorversion = 2;
349         $minorversion = 4;
350         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
351             $minorversion);
352         array_push($this->files['scripts'], ...$libfiles['scripts']);
353         array_push($this->files['styles'], ...$libfiles['styles']);
355         $this->h5p_file_storage->saveLibrary($lib);
357         $this->assertCount(2, $this->files['scripts']);
358         $this->assertCount(2, $this->files['styles']);
360         $key = 'testhashkey';
362         $this->h5p_file_storage->cacheAssets($this->files, $key);
364         $testarray = $this->h5p_file_storage->getCachedAssets($key);
365         $this->assertCount(1, $testarray['scripts']);
366         $this->assertCount(1, $testarray['styles']);
367         $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.js';
368         $this->assertEquals($expectedfile, $testarray['scripts'][0]->path);
369         $expectedfile = '/' . file_storage::CACHED_ASSETS_FILEAREA . '/' . $key . '.css';
370         $this->assertEquals($expectedfile, $testarray['styles'][0]->path);
371     }
373     /**
374      * Test that cache files in the files system can be removed.
375      */
376     public function test_deleteCachedAssets(): void {
377         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
379         $machinename = 'TestLib';
380         $majorversion = 1;
381         $minorversion = 0;
382         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
383             $minorversion);
384         array_push($this->files['scripts'], ...$libfiles['scripts']);
385         array_push($this->files['styles'], ...$libfiles['styles']);
387         // Now run the API call.
388         $this->h5p_file_storage->saveLibrary($lib);
390         // Second library.
391         $basedirectory = $this->h5p_tempath . '/' . 'supertest-2.4';
393         $this->libraryid++;
394         $machinename = 'SuperTest';
395         $majorversion = 2;
396         $minorversion = 4;
397         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
398             $minorversion);
399         array_push($this->files['scripts'], ...$libfiles['scripts']);
400         array_push($this->files['styles'], ...$libfiles['styles']);
402         $this->h5p_file_storage->saveLibrary($lib);
404         $this->assertCount(2, $this->files['scripts']);
405         $this->assertCount(2, $this->files['styles']);
407         $key = 'testhashkey';
409         $this->h5p_file_storage->cacheAssets($this->files, $key);
411         $testarray = $this->h5p_file_storage->getCachedAssets($key);
412         $this->assertCount(1, $testarray['scripts']);
413         $this->assertCount(1, $testarray['styles']);
415         // Time to delete.
416         $this->h5p_file_storage->deleteCachedAssets([$key]);
417         $testarray = $this->h5p_file_storage->getCachedAssets($key);
418         $this->assertNull($testarray);
419     }
421     /**
422      * Retrieve content from a file given a specific path.
423      */
424     public function test_getContent() {
425         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
427         $machinename = 'TestLib';
428         $majorversion = 1;
429         $minorversion = 0;
430         [$lib, $libfiles] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
431             $minorversion);
432         array_push($this->files['scripts'], ...$libfiles['scripts']);
433         array_push($this->files['styles'], ...$libfiles['styles']);
435         // Now run the API call.
436         $this->h5p_file_storage->saveLibrary($lib);
438         $content = $this->h5p_file_storage->getContent($this->files['scripts'][0]->path);
439         // The file content is created based on the file system path (\core_h5p_generator::create_file).
440         $expectedcontent = hash("md5", $basedirectory. '/' . 'scripts' . '/' . 'testlib.min.js');
442         $this->assertEquals($expectedcontent, $content);
443     }
445     /**
446      * Test that an upgrade script can be found on the file system.
447      */
448     public function test_getUpgradeScript() {
449         // Upload an upgrade file.
450         $machinename = 'TestLib';
451         $majorversion = 3;
452         $minorversion = 1;
453         $filepath = '/' . "{$machinename}-{$majorversion}.{$minorversion}" . '/';
454         $fs = get_file_storage();
455         $filerecord = [
456             'contextid' => \context_system::instance()->id,
457             'component' => file_storage::COMPONENT,
458             'filearea' => file_storage::LIBRARY_FILEAREA,
459             'itemid' => 15,
460             'filepath' => $filepath,
461             'filename' => 'upgrade.js'
462         ];
463         $filestorage = new file_storage();
464         $fs->create_file_from_string($filerecord, 'test string info');
465         $expectedfilepath = DIRECTORY_SEPARATOR . file_storage::LIBRARY_FILEAREA . $filepath . 'upgrade.js';
466         $this->assertEquals($expectedfilepath, $filestorage->getUpgradeScript($machinename, $majorversion, $minorversion));
467         $this->assertNull($filestorage->getUpgradeScript($machinename, $majorversion, 7));
468     }
470     /**
471      * Test that information from a source can be saved to the specified path.
472      * The zip file has the following contents
473      * - h5ptest
474      * |- content
475      * |     |- content.json
476      * |- testFont
477      * |     |- testfont.min.css
478      * |- testJavaScript
479      * |     |- testscript.min.js
480      * |- h5p.json
481      */
482     public function test_saveFileFromZip() {
484         $ziparchive = new zip_archive();
485         $path = __DIR__ . '/fixtures/h5ptest.zip';
486         $result = $ziparchive->open($path, file_archive::OPEN);
488         $files = $ziparchive->list_files();
489         foreach ($files as $file) {
490             if (!$file->is_directory) {
491                 $stream = $ziparchive->get_stream($file->index);
492                 $items = explode(DIRECTORY_SEPARATOR, $file->pathname);
493                 array_shift($items);
494                 $path = implode(DIRECTORY_SEPARATOR, $items);
495                 $this->h5p_file_storage->saveFileFromZip($this->h5p_tempath, $path, $stream);
496                 $filestocheck[] = $path;
497             }
498         }
499         $ziparchive->close();
501         foreach ($filestocheck as $filetocheck) {
502             $pathtocheck = $this->h5p_tempath .'/'. $filetocheck;
503             $this->assertFileExists($pathtocheck);
504         }
505     }
507     /**
508      * Test that a library is fully deleted from the file system
509      */
510     public function test_delete_library() {
512         $basedirectory = $this->h5p_tempath . '/' . 'test-1.0';
514         $machinename = 'TestLib';
515         $majorversion = 1;
516         $minorversion = 0;
517         [$lib, $files] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
518             $minorversion);
520         // Now run the API call.
521         $this->h5p_file_storage->saveLibrary($lib);
523         // Save a second library to ensure we aren't deleting all libraries, but just the one specified.
524         $basedirectory = $this->h5p_tempath . '/' . 'awesomelib-2.1';
526         $this->libraryid++;
527         $machinename = 'AwesomeLib';
528         $majorversion = 2;
529         $minorversion = 1;
530         [$lib2, $files2] = $this->h5p_generator->create_library($basedirectory, $this->libraryid, $machinename, $majorversion,
531             $minorversion);
533         // Now run the API call.
534         $this->h5p_file_storage->saveLibrary($lib2);
536         $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
537                 file_storage::LIBRARY_FILEAREA);
538         $this->assertCount(14, $files);
540         $this->h5p_file_storage->delete_library($lib);
542         // Let's look at the records.
543         $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
544                 file_storage::LIBRARY_FILEAREA);
545         $this->assertCount(7, $files);
547         // Check that the db count is still the same after setting the libraryId to false.
548         $lib['libraryId'] = false;
549         $this->h5p_file_storage->delete_library($lib);
551         $files =  $this->h5p_fs_fs->get_area_files($this->h5p_fs_context->id, file_storage::COMPONENT,
552                 file_storage::LIBRARY_FILEAREA);
553         $this->assertCount(7, $files);
554     }