Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

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