1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Testing the H5PFrameworkInterface interface implementation.
19 *
20 * @package    core_h5p
21 * @category   test
22 * @copyright  2019 Mihail Geshoski <mihail@moodle.com>
23 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26namespace core_h5p;
27
28use core_collator;
29use Moodle\H5PCore;
30use Moodle\H5PDisplayOptionBehaviour;
31
32/**
33 *
34 * Test class covering the H5PFrameworkInterface interface implementation.
35 *
36 * @package    core_h5p
37 * @copyright  2019 Mihail Geshoski <mihail@moodle.com>
38 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 * @runTestsInSeparateProcesses
40 */
41class framework_test extends \advanced_testcase {
42
43    /** @var \core_h5p\framework */
44    private $framework;
45
46    /**
47     * Set up function for tests.
48     */
49    public function setUp(): void {
50        $factory = new \core_h5p\factory();
51        $this->framework = $factory->get_framework();
52    }
53
54    /**
55     * Test the behaviour of getPlatformInfo().
56     */
57    public function test_getPlatformInfo() {
58        global $CFG;
59
60        $platforminfo = $this->framework->getPlatformInfo();
61
62        $expected = array(
63            'name' => 'Moodle',
64            'version' => $CFG->version,
65            'h5pVersion' => $CFG->version
66        );
67
68        $this->assertEquals($expected, $platforminfo);
69    }
70
71    /**
72     * Test the behaviour of fetchExternalData() when the store path is not defined.
73     *
74     * This test is intensive and requires downloading content of an external file,
75     * therefore it might take longer time to execute.
76     * In order to execute this test PHPUNIT_LONGTEST should be set to true in phpunit.xml or directly in config.php.
77     */
78    public function test_fetchExternalData_no_path_defined() {
79
80        if (!PHPUNIT_LONGTEST) {
81            $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
82        }
83
84        $this->resetAfterTest();
85
86        $library = 'H5P.Accordion';
87        // Provide a valid URL to an external H5P content.
88        $url = $this->getExternalTestFileUrl('/'.$library.'.h5p');
89
90        // Test fetching an external H5P content without defining a path to where the file should be stored.
91        $data = $this->framework->fetchExternalData($url, null, true);
92
93        // The response should not be empty and return true if the file was successfully downloaded.
94        $this->assertNotEmpty($data);
95        $this->assertTrue($data);
96
97        $h5pfolderpath = $this->framework->getUploadedH5pFolderPath();
98        // The uploaded file should exist on the filesystem.
99        $this->assertTrue(file_exists($h5pfolderpath . '.h5p'));
100    }
101
102    /**
103     * Test the behaviour of fetchExternalData() when the store path is defined.
104     *
105     * This test is intensive and requires downloading content of an external file,
106     * therefore it might take longer time to execute.
107     * In order to execute this test PHPUNIT_LONGTEST should be set to true in phpunit.xml or directly in config.php.
108     */
109    public function test_fetchExternalData_path_defined() {
110        global $CFG;
111
112        if (!PHPUNIT_LONGTEST) {
113            $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
114        }
115
116        $this->resetAfterTest();
117
118        $library = 'H5P.Accordion';
119        // Provide a valid URL to an external H5P content.
120        $url = $this->getExternalTestFileUrl('/'.$library.'.h5p');
121
122        $h5pfolderpath = $CFG->tempdir . uniqid('/h5p-');
123
124        $data = $this->framework->fetchExternalData($url, null, true, $h5pfolderpath . '.h5p');
125
126        // The response should not be empty and return true if the content has been successfully saved to a file.
127        $this->assertNotEmpty($data);
128        $this->assertTrue($data);
129
130        // The uploaded file should exist on the filesystem.
131        $this->assertTrue(file_exists($h5pfolderpath . '.h5p'));
132    }
133
134    /**
135     * Test the behaviour of fetchExternalData() when the URL is pointing to an external file that is
136     * not an h5p content.
137     *
138     * This test is intensive and requires downloading content of an external file,
139     * therefore it might take longer time to execute.
140     * In order to execute this test PHPUNIT_LONGTEST should be set to true in phpunit.xml or directly in config.php.
141     */
142    public function test_fetchExternalData_url_not_h5p() {
143
144        if (!PHPUNIT_LONGTEST) {
145            // This test is intensive and requires downloading the content of an external file.
146            $this->markTestSkipped('PHPUNIT_LONGTEST is not defined');
147        }
148
149        $this->resetAfterTest();
150
151        // Provide an URL to an external file that is not an H5P content file.
152        $url = $this->getExternalTestFileUrl('/h5pcontenttypes.json');
153
154        $data = $this->framework->fetchExternalData($url, null, true);
155
156        // The response should not be empty and return true if the content has been successfully saved to a file.
157        $this->assertNotEmpty($data);
158        $this->assertTrue($data);
159
160        // The uploaded file should exist on the filesystem with it's original extension.
161        // NOTE: The file would be later validated by the H5P Validator.
162        $h5pfolderpath = $this->framework->getUploadedH5pFolderPath();
163        $this->assertTrue(file_exists($h5pfolderpath . '.json'));
164    }
165
166    /**
167     * Test the behaviour of fetchExternalData() when the URL is invalid.
168     */
169    public function test_fetchExternalData_url_invalid() {
170        // Provide an invalid URL to an external file.
171        $url = "someprotocol://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
172
173        $data = $this->framework->fetchExternalData($url, null, true);
174
175        // The response should be empty.
176        $this->assertEmpty($data);
177    }
178
179    /**
180     * Test the behaviour of setLibraryTutorialUrl().
181     */
182    public function test_setLibraryTutorialUrl() {
183        global $DB;
184
185        $this->resetAfterTest();
186
187        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
188
189        // Create several libraries records.
190        $lib1 = $generator->create_library_record('Library1', 'Lib1', 1, 0, 1, '', null, 'http://tutorial1.org',
191            'http://example.org');
192        $lib2 = $generator->create_library_record('Library2', 'Lib2', 2, 0, 1, '', null, 'http://tutorial2.org');
193        $lib3 = $generator->create_library_record('Library3', 'Lib3', 3, 0);
194
195        // Check only lib1 tutorial URL is updated.
196        $url = 'https://newtutorial.cat';
197        $this->framework->setLibraryTutorialUrl($lib1->machinename, $url);
198
199        $libraries = $DB->get_records('h5p_libraries');
200        $this->assertEquals($libraries[$lib1->id]->tutorial, $url);
201        $this->assertNotEquals($libraries[$lib2->id]->tutorial, $url);
202
203        // Check lib1 tutorial URL is set to null.
204        $this->framework->setLibraryTutorialUrl($lib1->machinename, null);
205
206        $libraries = $DB->get_records('h5p_libraries');
207        $this->assertCount(3, $libraries);
208        $this->assertNull($libraries[$lib1->id]->tutorial);
209
210        // Check no tutorial URL is set if library name doesn't exist.
211        $this->framework->setLibraryTutorialUrl('Unexisting library', $url);
212
213        $libraries = $DB->get_records('h5p_libraries');
214        $this->assertCount(3, $libraries);
215        $this->assertNull($libraries[$lib1->id]->tutorial);
216        $this->assertEquals($libraries[$lib2->id]->tutorial, 'http://tutorial2.org');
217        $this->assertNull($libraries[$lib3->id]->tutorial);
218
219        // Check tutorial is set as expected when it was null.
220        $this->framework->setLibraryTutorialUrl($lib3->machinename, $url);
221
222        $libraries = $DB->get_records('h5p_libraries');
223        $this->assertEquals($libraries[$lib3->id]->tutorial, $url);
224        $this->assertNull($libraries[$lib1->id]->tutorial);
225        $this->assertEquals($libraries[$lib2->id]->tutorial, 'http://tutorial2.org');
226    }
227
228    /**
229     * Test the behaviour of setErrorMessage().
230     */
231    public function test_setErrorMessage() {
232        // Set an error message and an error code.
233        $message = "Error message";
234        $code = '404';
235
236        // Set an error message.
237        $this->framework->setErrorMessage($message, $code);
238
239        // Get the error messages.
240        $errormessages = $this->framework->getMessages('error');
241
242        $expected = new \stdClass();
243        $expected->code = 404;
244        $expected->message = 'Error message';
245
246        $this->assertEquals($expected, $errormessages[0]);
247    }
248
249    /**
250     * Test the behaviour of setInfoMessage().
251     */
252    public function test_setInfoMessage() {
253        $message = "Info message";
254
255        // Set an info message.
256        $this->framework->setInfoMessage($message);
257
258        // Get the info messages.
259        $infomessages = $this->framework->getMessages('info');
260
261        $expected = 'Info message';
262
263        $this->assertEquals($expected, $infomessages[0]);
264    }
265
266    /**
267     * Test the behaviour of getMessages() when requesting the info messages.
268     */
269    public function test_getMessages_info() {
270        // Set an info message.
271        $this->framework->setInfoMessage("Info message");
272        // Set an error message.
273        $this->framework->setErrorMessage("Error message 1", 404);
274
275        // Get the info messages.
276        $infomessages = $this->framework->getMessages('info');
277
278        $expected = 'Info message';
279
280        // Make sure that only the info message has been returned.
281        $this->assertCount(1, $infomessages);
282        $this->assertEquals($expected, $infomessages[0]);
283
284        $infomessages = $this->framework->getMessages('info');
285
286        // Make sure the info messages have now been removed.
287        $this->assertEmpty($infomessages);
288    }
289
290    /**
291     * Test the behaviour of getMessages() when requesting the error messages.
292     */
293    public function test_getMessages_error() {
294        // Set an info message.
295        $this->framework->setInfoMessage("Info message");
296        // Set an error message.
297        $this->framework->setErrorMessage("Error message 1", 404);
298        // Set another error message.
299        $this->framework->setErrorMessage("Error message 2", 403);
300
301        // Get the error messages.
302        $errormessages = $this->framework->getMessages('error');
303
304        // Make sure that only the error messages are being returned.
305        $this->assertEquals(2, count($errormessages));
306
307        $expected1 = (object) [
308            'code' => 404,
309            'message' => 'Error message 1'
310        ];
311
312        $expected2 = (object) [
313            'code' => 403,
314            'message' => 'Error message 2'
315        ];
316
317        $this->assertEquals($expected1, $errormessages[0]);
318        $this->assertEquals($expected2, $errormessages[1]);
319
320        $errormessages = $this->framework->getMessages('error');
321
322        // Make sure the info messages have now been removed.
323        $this->assertEmpty($errormessages);
324    }
325
326    /**
327     * Test the behaviour of t() when translating existing string that does not require any arguments.
328     */
329    public function test_t_existing_string_no_args() {
330        // Existing language string without passed arguments.
331        $translation = $this->framework->t('No copyright information available for this content.');
332
333        // Make sure the string translation has been returned.
334        $this->assertEquals('No copyright information available for this content.', $translation);
335    }
336
337    /**
338     * Test the behaviour of t() when translating existing string that does require parameters.
339     */
340    public function test_t_existing_string_args() {
341        // Existing language string with passed arguments.
342        $translation = $this->framework->t('Illegal option %option in %library',
343            ['%option' => 'example', '%library' => 'Test library']);
344
345        // Make sure the string translation has been returned.
346        $this->assertEquals('Illegal option example in Test library', $translation);
347    }
348
349    /**
350     * Test the behaviour of t() when translating non-existent string.
351     */
352    public function test_t_non_existent_string() {
353        // Non-existing language string.
354        $message = 'Random message %option';
355
356        $translation = $this->framework->t($message);
357
358        // Make sure a debugging message is triggered.
359        $this->assertDebuggingCalled("String translation cannot be found. Please add a string definition for '" .
360            $message . "' in the core_h5p component.");
361        // As the string does not exist in the mapping array, make sure the passed message is returned.
362        $this->assertEquals($message, $translation);
363    }
364
365    /**
366     * Test the behaviour of getLibraryFileUrl() when requesting a file URL from an existing library and
367     * the folder name is parsable.
368     **/
369    public function test_getLibraryFileUrl() {
370        global $CFG;
371
372        $this->resetAfterTest();
373
374        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
375        // Create a library record.
376        $lib = $generator->create_library_record('Library', 'Lib', 1, 1);
377
378        $expected = "{$CFG->wwwroot}/pluginfile.php/1/core_h5p/libraries/{$lib->id}/Library-1.1/library.json";
379
380        // Get the URL of a file from an existing library. The provided folder name is parsable.
381        $actual = $this->framework->getLibraryFileUrl('Library-1.1', 'library.json');
382
383        // Make sure the expected URL is returned.
384        $this->assertEquals($expected, $actual);
385    }
386
387    /**
388     * Test the behaviour of getLibraryFileUrl() when requesting a file URL from a non-existent library and
389     * the folder name is parsable.
390     **/
391    public function test_getLibraryFileUrl_non_existent_library() {
392        $this->resetAfterTest();
393
394        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
395        // Create a library record.
396        $generator->create_library_record('Library', 'Lib', 1, 1);
397
398        // Get the URL of a file from a non-existent library. The provided folder name is parsable.
399        $actual = $this->framework->getLibraryFileUrl('Library2-1.1', 'library.json');
400
401        // Make sure a debugging message is triggered.
402        $this->assertDebuggingCalled('The library "Library2-1.1" does not exist.');
403
404        // Make sure that an URL is not returned.
405        $this->assertEquals(null, $actual);
406    }
407
408    /**
409     * Test the behaviour of getLibraryFileUrl() when requesting a file URL from an existing library and
410     * the folder name is not parsable.
411     **/
412    public function test_getLibraryFileUrl_not_parsable_folder_name() {
413        $this->resetAfterTest();
414
415        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
416        // Create a library record.
417        $generator->create_library_record('Library', 'Lib', 1, 1);
418
419        // Get the URL of a file from an existing library. The provided folder name is not parsable.
420        $actual = $this->framework->getLibraryFileUrl('Library1.1', 'library.json');
421
422        // Make sure a debugging message is triggered.
423        $this->assertDebuggingCalled(
424            'The provided string value "Library1.1" is not a valid name for a library folder.');
425
426        // Make sure that an URL is not returned.
427        $this->assertEquals(null, $actual);
428    }
429
430    /**
431     * Test the behaviour of getLibraryFileUrl() when requesting a file URL from a library that has multiple
432     * versions and the folder name is parsable.
433     **/
434    public function test_getLibraryFileUrl_library_has_multiple_versions() {
435        global $CFG;
436
437        $this->resetAfterTest();
438
439        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
440        // Create library records with a different minor version.
441        $lib1 = $generator->create_library_record('Library', 'Lib', 1, 1);
442        $lib2 = $generator->create_library_record('Library', 'Lib', 1, 3);
443
444        $expected = "{$CFG->wwwroot}/pluginfile.php/1/core_h5p/libraries/{$lib2->id}/Library-1.3/library.json";
445
446        // Get the URL of a file from an existing library (Library 1.3). The provided folder name is parsable.
447        $actual = $this->framework->getLibraryFileUrl('Library-1.3', 'library.json');
448
449        // Make sure the proper URL (from the requested library version) is returned.
450        $this->assertEquals($expected, $actual);
451    }
452
453    /**
454     * Test the behaviour of getLibraryFileUrl() when requesting a file URL from a library that has multiple
455     * patch versions and the folder name is parsable.
456     **/
457    public function test_getLibraryFileUrl_library_has_multiple_patch_versions() {
458        global $CFG;
459
460        $this->resetAfterTest();
461
462        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
463        // Create library records with a different patch version.
464        $lib1 = $generator->create_library_record('Library', 'Lib', 1, 1, 2);
465        $lib2 = $generator->create_library_record('Library', 'Lib', 1, 1, 4);
466        $lib3 = $generator->create_library_record('Library', 'Lib', 1, 1, 3);
467
468        $expected = "{$CFG->wwwroot}/pluginfile.php/1/core_h5p/libraries/{$lib2->id}/Library-1.1/library.json";
469
470        // Get the URL of a file from an existing library. The provided folder name is parsable.
471        $actual = $this->framework->getLibraryFileUrl('Library-1.1', 'library.json');
472
473        // Make sure the proper URL (from the latest library patch) is returned.
474        $this->assertEquals($expected, $actual);
475    }
476
477    /**
478     * Test the behaviour of getLibraryFileUrl() when requesting a file URL from a sub-folder
479     * of an existing library and the folder name is parsable.
480     **/
481    public function test_getLibraryFileUrl_library_subfolder() {
482        global $CFG;
483
484        $this->resetAfterTest();
485
486        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
487        // Create a library record.
488        $lib = $generator->create_library_record('Library', 'Lib', 1, 1);
489
490        $expected = "{$CFG->wwwroot}/pluginfile.php/1/core_h5p/libraries/{$lib->id}/Library-1.1/css/example.css";
491
492        // Get the URL of a file from a sub-folder from an existing library. The provided folder name is parsable.
493        $actual = $this->framework->getLibraryFileUrl('Library-1.1/css', 'example.css');
494
495        // Make sure the proper URL is returned.
496        $this->assertEquals($expected, $actual);
497    }
498
499    /**
500     * Test the behaviour of loadAddons().
501     */
502    public function test_loadAddons() {
503        $this->resetAfterTest();
504
505        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
506
507        // Create a Library addon (1.1).
508        $generator->create_library_record('Library', 'Lib', 1, 1, 2,
509            '', '/regex1/');
510        // Create a Library addon (1.3).
511        $generator->create_library_record('Library', 'Lib', 1, 3, 2,
512            '', '/regex2/');
513        // Create a Library addon (1.2).
514        $generator->create_library_record('Library', 'Lib', 1, 2, 2,
515            '', '/regex3/');
516        // Create a Library1 addon (1.2)
517        $generator->create_library_record('Library1', 'Lib1', 1, 2, 2,
518            '', '/regex11/');
519
520        // Load the latest version of each addon.
521        $addons = $this->framework->loadAddons();
522
523        // The addons array should return 2 results (Library and Library1 addon).
524        $this->assertCount(2, $addons);
525
526        // Ensure the addons array is consistently ordered before asserting their contents.
527        core_collator::asort_array_of_arrays_by_key($addons, 'machineName');
528        [$addonone, $addontwo] = array_values($addons);
529
530        // Make sure the version 1.3 is the latest 'Library' addon version.
531        $this->assertEquals('Library', $addonone['machineName']);
532        $this->assertEquals(1, $addonone['majorVersion']);
533        $this->assertEquals(3, $addonone['minorVersion']);
534
535        // Make sure the version 1.2 is the latest 'Library1' addon version.
536        $this->assertEquals('Library1', $addontwo['machineName']);
537        $this->assertEquals(1, $addontwo['majorVersion']);
538        $this->assertEquals(2, $addontwo['minorVersion']);
539    }
540
541    /**
542     * Test the behaviour of loadLibraries().
543     */
544    public function test_loadLibraries() {
545        $this->resetAfterTest();
546
547        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
548
549        // Generate h5p related data.
550        $generator->generate_h5p_data();
551
552        // Load all libraries.
553        $libraries = $this->framework->loadLibraries();
554
555        // Make sure all libraries are returned.
556        $this->assertNotEmpty($libraries);
557        $this->assertCount(6, $libraries);
558        $this->assertEquals('MainLibrary', $libraries['MainLibrary'][0]->machine_name);
559        $this->assertEquals('1', $libraries['MainLibrary'][0]->major_version);
560        $this->assertEquals('0', $libraries['MainLibrary'][0]->minor_version);
561        $this->assertEquals('1', $libraries['MainLibrary'][0]->patch_version);
562    }
563
564    /**
565     * Test the behaviour of test_getLibraryId() when requesting an existing machine name.
566     */
567    public function test_getLibraryId_existing_machine_name() {
568        $this->resetAfterTest();
569
570        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
571
572        // Create a library.
573        $lib = $generator->create_library_record('Library', 'Lib', 1, 1, 2);
574
575        // Request the library ID of the library with machine name 'Library'.
576        $libraryid = $this->framework->getLibraryId('Library');
577
578        // Make sure the library ID is being returned.
579        $this->assertNotFalse($libraryid);
580        $this->assertEquals($lib->id, $libraryid);
581    }
582
583    /**
584     * Test the behaviour of test_getLibraryId() when requesting a non-existent machine name.
585     */
586    public function test_getLibraryId_non_existent_machine_name() {
587        $this->resetAfterTest();
588
589        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
590
591        // Create a library.
592        $generator->create_library_record('Library', 'Lib', 1, 1, 2);
593
594        // Request the library ID of the library with machinename => 'TestLibrary' (non-existent).
595        $libraryid = $this->framework->getLibraryId('TestLibrary');
596
597        // Make sure the library ID not being returned.
598        $this->assertFalse($libraryid);
599    }
600
601    /**
602     * Test the behaviour of test_getLibraryId() when requesting a non-existent major version.
603     */
604    public function test_getLibraryId_non_existent_major_version() {
605        $this->resetAfterTest();
606
607        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
608
609        // Create a library.
610        $generator->create_library_record('Library', 'Lib', 1, 1, 2);
611
612        // Request the library ID of the library with machine name => 'Library', majorversion => 2 (non-existent).
613        $libraryid = $this->framework->getLibraryId('Library', 2);
614
615        // Make sure the library ID not being returned.
616        $this->assertFalse($libraryid);
617    }
618
619    /**
620     * Test the behaviour of test_getLibraryId() when requesting a non-existent minor version.
621     */
622    public function test_getLibraryId_non_existent_minor_version() {
623        $this->resetAfterTest();
624
625        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
626
627        // Create a library.
628        $generator->create_library_record('Library', 'Lib', 1, 1, 2);
629
630        // Request the library ID of the library with machine name => 'Library',
631        // majorversion => 1,  minorversion => 2 (non-existent).
632        $libraryid = $this->framework->getLibraryId('Library', 1, 2);
633
634        // Make sure the library ID not being returned.
635        $this->assertFalse($libraryid);
636    }
637
638    /**
639     * Test the behaviour of isPatchedLibrary().
640     *
641     * @dataProvider test_isPatchedLibrary_provider
642     * @param array $libraryrecords Array containing data for the library creation
643     * @param array $testlibrary Array containing the test library data
644     * @param bool $expected The expectation whether the library is patched or not
645     **/
646    public function test_isPatchedLibrary(array $libraryrecords, array $testlibrary, bool $expected): void {
647        $this->resetAfterTest();
648
649        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
650
651        foreach ($libraryrecords as $library) {
652            call_user_func_array([$generator, 'create_library_record'], $library);
653        }
654
655        $this->assertEquals($expected, $this->framework->isPatchedLibrary($testlibrary));
656    }
657
658    /**
659     * Data provider for test_isPatchedLibrary().
660     *
661     * @return array
662     */
663    public function test_isPatchedLibrary_provider(): array {
664        return [
665            'Unpatched library. No different versioning' => [
666                [
667                    ['TestLibrary', 'Test', 1, 1, 2],
668                ],
669                [
670                    'machineName' => 'TestLibrary',
671                    'majorVersion' => 1,
672                    'minorVersion' => 1,
673                    'patchVersion' => 2
674                ],
675                false,
676            ],
677            'Major version identical; Minor version identical; Patch version newer' => [
678                [
679                    ['TestLibrary', 'Test', 1, 1, 2],
680                ],
681                [
682                    'machineName' => 'TestLibrary',
683                    'majorVersion' => 1,
684                    'minorVersion' => 1,
685                    'patchVersion' => 3
686                ],
687                true,
688            ],
689            'Major version identical; Minor version newer; Patch version newer' => [
690                [
691                    ['TestLibrary', 'Test', 1, 1, 2],
692                ],
693                [
694                    'machineName' => 'TestLibrary',
695                    'majorVersion' => 1,
696                    'minorVersion' => 2,
697                    'patchVersion' => 3
698                ],
699                false,
700            ],
701            'Major version identical; Minor version identical; Patch version older' => [
702                [
703                    ['TestLibrary', 'Test', 1, 1, 2],
704                ],
705                [
706                    'machineName' => 'TestLibrary',
707                    'majorVersion' => 1,
708                    'minorVersion' => 1,
709                    'patchVersion' => 1
710                ],
711                false,
712            ],
713            'Major version identical; Minor version newer; Patch version older' => [
714                [
715                    ['TestLibrary', 'Test', 1, 1, 2],
716                ],
717                [
718                    'machineName' => 'TestLibrary',
719                    'majorVersion' => 1,
720                    'minorVersion' => 2,
721                    'patchVersion' => 1
722                ],
723                false,
724            ],
725            'Major version newer; Minor version identical; Patch version older' => [
726                [
727                    ['TestLibrary', 'Test', 1, 1, 2],
728                ],
729                [
730                    'machineName' => 'TestLibrary',
731                    'majorVersion' => 2,
732                    'minorVersion' => 1,
733                    'patchVersion' => 1
734                ],
735                false,
736            ],
737            'Major version newer; Minor version identical; Patch version newer' => [
738                [
739                    ['TestLibrary', 'Test', 1, 1, 2],
740                ],
741                [
742                    'machineName' => 'TestLibrary',
743                    'majorVersion' => 2,
744                    'minorVersion' => 1,
745                    'patchVersion' => 3
746                ],
747                false,
748            ],
749
750            'Major version older; Minor version identical; Patch version older' => [
751                [
752                    ['TestLibrary', 'Test', 1, 1, 2],
753                ],
754                [
755                    'machineName' => 'TestLibrary',
756                    'majorVersion' => 0,
757                    'minorVersion' => 1,
758                    'patchVersion' => 1
759                ],
760                false,
761            ],
762            'Major version older; Minor version identical; Patch version newer' => [
763                [
764                    ['TestLibrary', 'Test', 1, 1, 2],
765                ],
766                [
767                    'machineName' => 'TestLibrary',
768                    'majorVersion' => 0,
769                    'minorVersion' => 1,
770                    'patchVersion' => 3
771                ],
772                false,
773            ],
774        ];
775    }
776
777    /**
778     * Test the behaviour of isInDevMode().
779     */
780    public function test_isInDevMode() {
781        $isdevmode = $this->framework->isInDevMode();
782
783        $this->assertFalse($isdevmode);
784    }
785
786    /**
787     * Test the behaviour of mayUpdateLibraries().
788     */
789    public function test_mayUpdateLibraries(): void {
790        global $DB;
791
792        $this->resetAfterTest();
793
794        // Create some users.
795        $contextsys = \context_system::instance();
796        $user = $this->getDataGenerator()->create_user();
797        $admin = get_admin();
798        $managerrole = $DB->get_record('role', ['shortname' => 'manager'], '*', MUST_EXIST);
799        $studentrole = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
800        $manager = $this->getDataGenerator()->create_user();
801        role_assign($managerrole->id, $manager->id, $contextsys);
802
803        // Create a course with a label and enrol the user.
804        $course = $this->getDataGenerator()->create_course();
805        $label = $this->getDataGenerator()->create_module('label', ['course' => $course->id]);
806        list(, $labelcm) = get_course_and_cm_from_instance($label->id, 'label');
807        $contextlabel = \context_module::instance($labelcm->id);
808        $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
809
810        // Create the .h5p file.
811        $path = __DIR__ . '/fixtures/h5ptest.zip';
812
813        // Admin and manager should have permission to update libraries.
814        $file = helper::create_fake_stored_file_from_path($path, $admin->id, $contextsys);
815        $this->framework->set_file($file);
816        $mayupdatelib = $this->framework->mayUpdateLibraries();
817        $this->assertTrue($mayupdatelib);
818
819        $file = helper::create_fake_stored_file_from_path($path, $manager->id, $contextsys);
820        $this->framework->set_file($file);
821        $mayupdatelib = $this->framework->mayUpdateLibraries();
822        $this->assertTrue($mayupdatelib);
823
824        // By default, normal user hasn't permission to update libraries (in both contexts, system and module label).
825        $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextsys);
826        $this->framework->set_file($file);
827        $mayupdatelib = $this->framework->mayUpdateLibraries();
828        $this->assertFalse($mayupdatelib);
829
830        $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextlabel);
831        $this->framework->set_file($file);
832        $mayupdatelib = $this->framework->mayUpdateLibraries();
833        $this->assertFalse($mayupdatelib);
834
835        // If the current user (admin) can update libraries, the method should return true (even if the file userid hasn't the
836        // required capabilility in the file context).
837        $file = helper::create_fake_stored_file_from_path($path, $admin->id, $contextlabel);
838        $this->framework->set_file($file);
839        $mayupdatelib = $this->framework->mayUpdateLibraries();
840        $this->assertTrue($mayupdatelib);
841
842        // If the update capability is assigned to the user, they should be able to update the libraries (only in the context
843        // where the capability has been assigned).
844        $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextlabel);
845        $this->framework->set_file($file);
846        $mayupdatelib = $this->framework->mayUpdateLibraries();
847        $this->assertFalse($mayupdatelib);
848        assign_capability('moodle/h5p:updatelibraries', CAP_ALLOW, $studentrole->id, $contextlabel);
849        $mayupdatelib = $this->framework->mayUpdateLibraries();
850        $this->assertTrue($mayupdatelib);
851        $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextsys);
852        $this->framework->set_file($file);
853        $mayupdatelib = $this->framework->mayUpdateLibraries();
854        $this->assertFalse($mayupdatelib);
855    }
856
857    /**
858     * Test the behaviour of get_file() and set_file().
859     */
860    public function test_get_file(): void {
861        $this->resetAfterTest();
862
863        // Create some users.
864        $contextsys = \context_system::instance();
865        $user = $this->getDataGenerator()->create_user();
866
867        // The H5P file.
868        $path = __DIR__ . '/fixtures/h5ptest.zip';
869
870        // An error should be raised when it's called before initialitzing it.
871        $this->expectException('coding_exception');
872        $this->expectExceptionMessage('Using get_file() before file is set');
873        $this->framework->get_file();
874
875        // Check the value when only path and user are set.
876        $file = helper::create_fake_stored_file_from_path($path, $user->id);
877        $this->framework->set_file($file);
878        $file = $this->framework->get_file();
879        $this->assertEquals($user->id, $$file->get_userid());
880        $this->assertEquals($contextsys->id, $file->get_contextid());
881
882        // Check the value when also the context is set.
883        $course = $this->getDataGenerator()->create_course();
884        $contextcourse = \context_course::instance($course->id);
885        $file = helper::create_fake_stored_file_from_path($path, $user->id, $contextcourse);
886        $this->framework->set_file($file);
887        $file = $this->framework->get_file();
888        $this->assertEquals($user->id, $$file->get_userid());
889        $this->assertEquals($contextcourse->id, $file->get_contextid());
890    }
891
892    /**
893     * Test the behaviour of saveLibraryData() when saving data for a new library.
894     */
895    public function test_saveLibraryData_new_library() {
896        global $DB;
897
898        $this->resetAfterTest();
899
900        $librarydata = array(
901            'title' => 'Test',
902            'machineName' => 'TestLibrary',
903            'majorVersion' => '1',
904            'minorVersion' => '0',
905            'patchVersion' => '2',
906            'runnable' => 1,
907            'fullscreen' => 1,
908            'preloadedJs' => array(
909                array(
910                    'path' => 'js/name.min.js'
911                )
912            ),
913            'preloadedCss' => array(
914                array(
915                    'path' => 'css/name.css'
916                )
917            ),
918            'dropLibraryCss' => array(
919                array(
920                    'machineName' => 'Name2'
921                )
922            )
923        );
924
925        // Create a new library.
926        $this->framework->saveLibraryData($librarydata);
927
928        $library = $DB->get_record('h5p_libraries', ['machinename' => $librarydata['machineName']]);
929
930        // Make sure the library data was properly saved.
931        $this->assertNotEmpty($library);
932        $this->assertNotEmpty($librarydata['libraryId']);
933        $this->assertEquals($librarydata['title'], $library->title);
934        $this->assertEquals($librarydata['machineName'], $library->machinename);
935        $this->assertEquals($librarydata['majorVersion'], $library->majorversion);
936        $this->assertEquals($librarydata['minorVersion'], $library->minorversion);
937        $this->assertEquals($librarydata['patchVersion'], $library->patchversion);
938        $this->assertEquals($librarydata['preloadedJs'][0]['path'], $library->preloadedjs);
939        $this->assertEquals($librarydata['preloadedCss'][0]['path'], $library->preloadedcss);
940        $this->assertEquals($librarydata['dropLibraryCss'][0]['machineName'], $library->droplibrarycss);
941    }
942
943    /**
944     * Test the behaviour of saveLibraryData() when saving (updating) data for an existing library.
945     */
946    public function test_saveLibraryData_existing_library() {
947        global $DB;
948
949        $this->resetAfterTest();
950
951        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
952
953        // Create a library record.
954        $library = $generator->create_library_record('TestLibrary', 'Test', 1, 0, 2);
955
956        $librarydata = array(
957            'libraryId' => $library->id,
958            'title' => 'Test1',
959            'machineName' => 'TestLibrary',
960            'majorVersion' => '1',
961            'minorVersion' => '2',
962            'patchVersion' => '2',
963            'runnable' => 1,
964            'fullscreen' => 1,
965            'preloadedJs' => array(
966                array(
967                    'path' => 'js/name.min.js'
968                )
969            ),
970            'preloadedCss' => array(
971                array(
972                    'path' => 'css/name.css'
973                )
974            ),
975            'dropLibraryCss' => array(
976                array(
977                    'machineName' => 'Name2'
978                )
979            )
980        );
981
982        // Update the library.
983        $this->framework->saveLibraryData($librarydata, false);
984
985        $library = $DB->get_record('h5p_libraries', ['machinename' => $librarydata['machineName']]);
986
987        // Make sure the library data was properly updated.
988        $this->assertNotEmpty($library);
989        $this->assertNotEmpty($librarydata['libraryId']);
990        $this->assertEquals($librarydata['title'], $library->title);
991        $this->assertEquals($librarydata['machineName'], $library->machinename);
992        $this->assertEquals($librarydata['majorVersion'], $library->majorversion);
993        $this->assertEquals($librarydata['minorVersion'], $library->minorversion);
994        $this->assertEquals($librarydata['patchVersion'], $library->patchversion);
995        $this->assertEquals($librarydata['preloadedJs'][0]['path'], $library->preloadedjs);
996        $this->assertEquals($librarydata['preloadedCss'][0]['path'], $library->preloadedcss);
997        $this->assertEquals($librarydata['dropLibraryCss'][0]['machineName'], $library->droplibrarycss);
998    }
999
1000    /**
1001     * Test the behaviour of insertContent().
1002     */
1003    public function test_insertContent() {
1004        global $DB;
1005
1006        $this->resetAfterTest();
1007
1008        $content = array(
1009            'params' => json_encode(['param1' => 'Test']),
1010            'library' => array(
1011                'libraryId' => 1
1012            ),
1013            'disable' => 8
1014        );
1015
1016        // Insert h5p content.
1017        $contentid = $this->framework->insertContent($content);
1018
1019        // Get the entered content from the db.
1020        $dbcontent = $DB->get_record('h5p', ['id' => $contentid]);
1021
1022        // Make sure the h5p content was properly inserted.
1023        $this->assertNotEmpty($dbcontent);
1024        $this->assertEquals($content['params'], $dbcontent->jsoncontent);
1025        $this->assertEquals($content['library']['libraryId'], $dbcontent->mainlibraryid);
1026        $this->assertEquals($content['disable'], $dbcontent->displayoptions);
1027    }
1028
1029    /**
1030     * Test the behaviour of insertContent().
1031     */
1032    public function test_insertContent_latestlibrary() {
1033        global $DB;
1034
1035        $this->resetAfterTest();
1036
1037        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1038        // Create a library record.
1039        $lib = $generator->create_library_record('TestLibrary', 'Test', 1, 1, 2);
1040
1041        $content = array(
1042            'params' => json_encode(['param1' => 'Test']),
1043            'library' => array(
1044                'libraryId' => 0,
1045                'machineName' => 'TestLibrary',
1046            ),
1047            'disable' => 8
1048        );
1049
1050        // Insert h5p content.
1051        $contentid = $this->framework->insertContent($content);
1052
1053        // Get the entered content from the db.
1054        $dbcontent = $DB->get_record('h5p', ['id' => $contentid]);
1055
1056        // Make sure the h5p content was properly inserted.
1057        $this->assertNotEmpty($dbcontent);
1058        $this->assertEquals($content['params'], $dbcontent->jsoncontent);
1059        $this->assertEquals($content['disable'], $dbcontent->displayoptions);
1060        // As the libraryId was empty, the latest library has been used.
1061        $this->assertEquals($lib->id, $dbcontent->mainlibraryid);
1062    }
1063
1064    /**
1065     * Test the behaviour of updateContent().
1066     */
1067    public function test_updateContent() {
1068        global $DB;
1069
1070        $this->resetAfterTest();
1071
1072        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1073
1074        // Create a library record.
1075        $lib = $generator->create_library_record('TestLibrary', 'Test', 1, 1, 2);
1076
1077        // Create an h5p content with 'TestLibrary' as it's main library.
1078        $contentid = $generator->create_h5p_record($lib->id);
1079
1080        $content = array(
1081            'id' => $contentid,
1082            'params' => json_encode(['param2' => 'Test2']),
1083            'library' => array(
1084                'libraryId' => $lib->id
1085            ),
1086            'disable' => 8
1087        );
1088
1089        // Update the h5p content.
1090        $this->framework->updateContent($content);
1091
1092        $h5pcontent = $DB->get_record('h5p', ['id' => $contentid]);
1093
1094        // Make sure the h5p content was properly updated.
1095        $this->assertNotEmpty($h5pcontent);
1096        $this->assertEquals($content['params'], $h5pcontent->jsoncontent);
1097        $this->assertEquals($content['library']['libraryId'], $h5pcontent->mainlibraryid);
1098        $this->assertEquals($content['disable'], $h5pcontent->displayoptions);
1099    }
1100
1101    /**
1102     * Test the behaviour of saveLibraryDependencies().
1103     */
1104    public function test_saveLibraryDependencies() {
1105        global $DB;
1106
1107        $this->resetAfterTest();
1108
1109        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1110
1111        // Create a library 'Library'.
1112        $library = $generator->create_library_record('Library', 'Title');
1113        // Create a library 'DependencyLibrary1'.
1114        $dependency1 = $generator->create_library_record('DependencyLibrary1', 'DependencyTitle1');
1115        // Create a library 'DependencyLibrary2'.
1116        $dependency2 = $generator->create_library_record('DependencyLibrary2', 'DependencyTitle2');
1117
1118        $dependencies = array(
1119            array(
1120                'machineName' => $dependency1->machinename,
1121                'majorVersion' => $dependency1->majorversion,
1122                'minorVersion' => $dependency1->minorversion
1123            ),
1124            array(
1125                'machineName' => $dependency2->machinename,
1126                'majorVersion' => $dependency2->majorversion,
1127                'minorVersion' => $dependency2->minorversion
1128            ),
1129        );
1130
1131        // Set 'DependencyLibrary1' and 'DependencyLibrary2' as library dependencies of 'Library'.
1132        $this->framework->saveLibraryDependencies($library->id, $dependencies, 'preloaded');
1133
1134        $libdependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library->id], 'id ASC');
1135
1136        // Make sure the library dependencies for 'Library' are properly set.
1137        $this->assertEquals(2, count($libdependencies));
1138        $this->assertEquals($dependency1->id, reset($libdependencies)->requiredlibraryid);
1139        $this->assertEquals($dependency2->id, end($libdependencies)->requiredlibraryid);
1140    }
1141
1142    /**
1143     * Test the behaviour of deleteContentData().
1144     */
1145    public function test_deleteContentData() {
1146        global $DB;
1147
1148        $this->resetAfterTest();
1149
1150        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1151
1152        // Generate some h5p related data.
1153        $data = $generator->generate_h5p_data();
1154        $h5pid = $data->h5pcontent->h5pid;
1155
1156        $h5pcontent = $DB->get_record('h5p', ['id' => $h5pid]);
1157        // Make sure the particular h5p content exists in the DB.
1158        $this->assertNotEmpty($h5pcontent);
1159
1160        // Get the h5p content libraries from the DB.
1161        $h5pcontentlibraries = $DB->get_records('h5p_contents_libraries', ['h5pid' => $h5pid]);
1162
1163        // Make sure the content libraries exists in the DB.
1164        $this->assertNotEmpty($h5pcontentlibraries);
1165        $this->assertCount(5, $h5pcontentlibraries);
1166
1167        // Delete the h5p content and it's related data.
1168        $this->framework->deleteContentData($h5pid);
1169
1170        $h5pcontent = $DB->get_record('h5p', ['id' => $h5pid]);
1171        $h5pcontentlibraries = $DB->get_record('h5p_contents_libraries', ['h5pid' => $h5pid]);
1172
1173        // The particular h5p content should no longer exist in the db.
1174        $this->assertEmpty($h5pcontent);
1175        // The particular content libraries should no longer exist in the db.
1176        $this->assertEmpty($h5pcontentlibraries);
1177    }
1178
1179    /**
1180     * Test the behaviour of deleteLibraryUsage().
1181     */
1182    public function test_deleteLibraryUsage() {
1183        global $DB;
1184
1185        $this->resetAfterTest();
1186
1187        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1188
1189        // Generate some h5p related data.
1190        $data = $generator->generate_h5p_data();
1191        $h5pid = $data->h5pcontent->h5pid;
1192
1193        // Get the h5p content libraries from the DB.
1194        $h5pcontentlibraries = $DB->get_records('h5p_contents_libraries', ['h5pid' => $h5pid]);
1195
1196        // The particular h5p content should have 5 content libraries.
1197        $this->assertNotEmpty($h5pcontentlibraries);
1198        $this->assertCount(5, $h5pcontentlibraries);
1199
1200        // Delete the h5p content and it's related data.
1201        $this->framework->deleteLibraryUsage($h5pid);
1202
1203        // Get the h5p content libraries from the DB.
1204        $h5pcontentlibraries = $DB->get_record('h5p_contents_libraries', ['h5pid' => $h5pid]);
1205
1206        // The particular h5p content libraries should no longer exist in the db.
1207        $this->assertEmpty($h5pcontentlibraries);
1208    }
1209
1210    /**
1211     * Test the behaviour of test_saveLibraryUsage().
1212     */
1213    public function test_saveLibraryUsage() {
1214        global $DB;
1215
1216        $this->resetAfterTest();
1217
1218        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1219
1220        // Create a library 'Library'.
1221        $library = $generator->create_library_record('Library', 'Title');
1222        // Create a library 'DependencyLibrary1'.
1223        $dependency1 = $generator->create_library_record('DependencyLibrary1', 'DependencyTitle1');
1224        // Create a library 'DependencyLibrary2'.
1225        $dependency2 = $generator->create_library_record('DependencyLibrary2', 'DependencyTitle2');
1226        // Create an h5p content with 'Library' as it's main library.
1227        $contentid = $generator->create_h5p_record($library->id);
1228
1229        $dependencies = array(
1230            array(
1231                'library' => array(
1232                    'libraryId' => $dependency1->id,
1233                    'machineName' => $dependency1->machinename,
1234                    'dropLibraryCss' => $dependency1->droplibrarycss
1235                ),
1236                'type' => 'preloaded',
1237                'weight' => 1
1238            ),
1239            array(
1240                'library' => array(
1241                    'libraryId' => $dependency2->id,
1242                    'machineName' => $dependency2->machinename,
1243                    'dropLibraryCss' => $dependency2->droplibrarycss
1244                ),
1245                'type' => 'preloaded',
1246                'weight' => 2
1247            ),
1248        );
1249
1250        // Save 'DependencyLibrary1' and 'DependencyLibrary2' as h5p content libraries.
1251        $this->framework->saveLibraryUsage($contentid, $dependencies);
1252
1253        // Get the h5p content libraries from the DB.
1254        $libdependencies = $DB->get_records('h5p_contents_libraries', ['h5pid' => $contentid], 'id ASC');
1255
1256        // Make sure that 'DependencyLibrary1' and 'DependencyLibrary2' are properly set as h5p content libraries.
1257        $this->assertEquals(2, count($libdependencies));
1258        $this->assertEquals($dependency1->id, reset($libdependencies)->libraryid);
1259        $this->assertEquals($dependency2->id, end($libdependencies)->libraryid);
1260    }
1261
1262    /**
1263     * Test the behaviour of getLibraryUsage() without skipping a particular h5p content.
1264     */
1265    public function test_getLibraryUsage_no_skip_content() {
1266        $this->resetAfterTest();
1267
1268        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1269
1270        // Generate h5p related data.
1271        $generateddata = $generator->generate_h5p_data();
1272        // The Id of the library 'Library1'.
1273        $library1id = $generateddata->lib1->data->id;
1274        // The Id of the library 'Library2'.
1275        $library2id = $generateddata->lib2->data->id;
1276        // The Id of the library 'Library5'.
1277        $library5id = $generateddata->lib5->data->id;
1278
1279        // Get the library usage for 'Library1' (do not skip content).
1280        $data = $this->framework->getLibraryUsage($library1id);
1281
1282        $expected = array(
1283            'content' => 1,
1284            'libraries' => 1
1285        );
1286
1287        // Make sure 'Library1' is used by 1 content and is a dependency to 1 library.
1288        $this->assertEquals($expected, $data);
1289
1290        // Get the library usage for 'Library2' (do not skip content).
1291        $data = $this->framework->getLibraryUsage($library2id);
1292
1293        $expected = array(
1294            'content' => 1,
1295            'libraries' => 2,
1296        );
1297
1298        // Make sure 'Library2' is used by 1 content and is a dependency to 2 libraries.
1299        $this->assertEquals($expected, $data);
1300
1301         // Get the library usage for 'Library5' (do not skip content).
1302        $data = $this->framework->getLibraryUsage($library5id);
1303
1304        $expected = array(
1305            'content' => 0,
1306            'libraries' => 1,
1307        );
1308
1309        // Make sure 'Library5' is not used by any content and is a dependency to 1 library.
1310        $this->assertEquals($expected, $data);
1311    }
1312
1313    /**
1314     * Test the behaviour of getLibraryUsage() when skipping a particular content.
1315     */
1316    public function test_getLibraryUsage_skip_content() {
1317        $this->resetAfterTest();
1318
1319        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1320
1321        // Generate h5p related data.
1322        $generateddata = $generator->generate_h5p_data();
1323        // The Id of the library 'Library1'.
1324        $library1id = $generateddata->lib1->data->id;
1325
1326        // Get the library usage for 'Library1' (skip content).
1327        $data = $this->framework->getLibraryUsage($library1id, true);
1328        $expected = array(
1329            'content' => -1,
1330            'libraries' => 1,
1331        );
1332
1333        // Make sure 'Library1' is a dependency to 1 library.
1334        $this->assertEquals($expected, $data);
1335    }
1336
1337    /**
1338     * Test the behaviour of loadLibrary() when requesting an existing library.
1339     */
1340    public function test_loadLibrary_existing_library() {
1341        $this->resetAfterTest();
1342
1343        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1344
1345        // Generate h5p related data.
1346        $generateddata = $generator->generate_h5p_data();
1347        // The library data of 'Library1'.
1348        $library1 = $generateddata->lib1->data;
1349        // The library data of 'Library5'.
1350        $library5 = $generateddata->lib5->data;
1351
1352        // The preloaded dependencies.
1353        $preloadeddependencies = array();
1354
1355        foreach ($generateddata->lib1->dependencies as $preloadeddependency) {
1356            $preloadeddependencies[] = array(
1357                'machineName' => $preloadeddependency->machinename,
1358                'majorVersion' => $preloadeddependency->majorversion,
1359                'minorVersion' => $preloadeddependency->minorversion
1360            );
1361        }
1362
1363        // Create a dynamic dependency.
1364        $generator->create_library_dependency_record($library1->id, $library5->id, 'dynamic');
1365
1366        $dynamicdependencies[] = array(
1367            'machineName' => $library5->machinename,
1368            'majorVersion' => $library5->majorversion,
1369            'minorVersion' => $library5->minorversion
1370        );
1371
1372        // Load 'Library1' data.
1373        $data = $this->framework->loadLibrary($library1->machinename, $library1->majorversion,
1374            $library1->minorversion);
1375
1376        $expected = array(
1377            'libraryId' => $library1->id,
1378            'title' => $library1->title,
1379            'machineName' => $library1->machinename,
1380            'majorVersion' => $library1->majorversion,
1381            'minorVersion' => $library1->minorversion,
1382            'patchVersion' => $library1->patchversion,
1383            'runnable' => $library1->runnable,
1384            'fullscreen' => $library1->fullscreen,
1385            'embedTypes' => $library1->embedtypes,
1386            'preloadedJs' => $library1->preloadedjs,
1387            'preloadedCss' => $library1->preloadedcss,
1388            'dropLibraryCss' => $library1->droplibrarycss,
1389            'semantics' => $library1->semantics,
1390            'preloadedDependencies' => $preloadeddependencies,
1391            'dynamicDependencies' => $dynamicdependencies
1392        );
1393
1394        // Make sure the 'Library1' data is properly loaded.
1395        $this->assertEquals($expected, $data);
1396    }
1397
1398    /**
1399     * Test the behaviour of loadLibrary() when requesting a non-existent library.
1400     */
1401    public function test_loadLibrary_non_existent_library() {
1402        $this->resetAfterTest();
1403
1404        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1405
1406        // Generate h5p related data.
1407        $generator->generate_h5p_data();
1408
1409        // Attempt to load a non-existent library.
1410        $data = $this->framework->loadLibrary('MissingLibrary', 1, 2);
1411
1412        // Make sure nothing is loaded.
1413        $this->assertFalse($data);
1414    }
1415
1416    /**
1417     * Test the behaviour of loadLibrarySemantics().
1418     *
1419     * @dataProvider test_loadLibrarySemantics_provider
1420     * @param array $libraryrecords Array containing data for the library creation
1421     * @param array $testlibrary Array containing the test library data
1422     * @param string $expected The expected semantics value
1423     **/
1424    public function test_loadLibrarySemantics(array $libraryrecords, array $testlibrary, string $expected): void {
1425        $this->resetAfterTest();
1426
1427        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1428
1429        foreach ($libraryrecords as $library) {
1430            call_user_func_array([$generator, 'create_library_record'], $library);
1431        }
1432
1433        $this->assertEquals($expected, $this->framework->loadLibrarySemantics(
1434            $testlibrary['machinename'], $testlibrary['majorversion'], $testlibrary['minorversion']));
1435    }
1436
1437    /**
1438     * Data provider for test_loadLibrarySemantics().
1439     *
1440     * @return array
1441     */
1442    public function test_loadLibrarySemantics_provider(): array {
1443
1444        $semantics = json_encode(
1445            [
1446                'type' => 'text',
1447                'name' => 'text',
1448                'label' => 'Plain text',
1449                'description' => 'Please add some text'
1450            ]
1451        );
1452
1453        return [
1454            'Library with semantics' => [
1455                [
1456                    ['Library1', 'Lib1', 1, 1, 2, $semantics],
1457                ],
1458                [
1459                    'machinename' => 'Library1',
1460                    'majorversion' => 1,
1461                    'minorversion' => 1
1462                ],
1463                $semantics,
1464            ],
1465            'Library without semantics' => [
1466                [
1467                    ['Library2', 'Lib2', 1, 2, 2, ''],
1468                ],
1469                [
1470                    'machinename' => 'Library2',
1471                    'majorversion' => 1,
1472                    'minorversion' => 2
1473                ],
1474                '',
1475            ]
1476        ];
1477    }
1478
1479    /**
1480     * Test the behaviour of alterLibrarySemantics().
1481     */
1482    public function test_alterLibrarySemantics() {
1483        global $DB;
1484
1485        $this->resetAfterTest();
1486
1487        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1488
1489        $semantics = json_encode(
1490            array(
1491                'type' => 'text',
1492                'name' => 'text',
1493                'label' => 'Plain text',
1494                'description' => 'Please add some text'
1495            )
1496        );
1497
1498        // Create a library 'Library1' with semantics.
1499        $library1 = $generator->create_library_record('Library1', 'Lib1', 1, 1, 2, $semantics);
1500
1501        $updatedsemantics = array(
1502            'type' => 'text',
1503            'name' => 'updated text',
1504            'label' => 'Updated text',
1505            'description' => 'Please add some text'
1506        );
1507
1508        // Alter the semantics of 'Library1'.
1509        $this->framework->alterLibrarySemantics($updatedsemantics, 'Library1', 1, 1);
1510
1511        // Get the semantics of 'Library1' from the DB.
1512        $currentsemantics = $DB->get_field('h5p_libraries', 'semantics', array('id' => $library1->id));
1513
1514        // The semantics for Library1 shouldn't be updated.
1515        $this->assertEquals($semantics, $currentsemantics);
1516    }
1517
1518    /**
1519     * Test the behaviour of deleteLibraryDependencies() when requesting to delete the
1520     * dependencies of an existing library.
1521     */
1522    public function test_deleteLibraryDependencies_existing_library() {
1523        global $DB;
1524
1525        $this->resetAfterTest();
1526
1527        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1528
1529        // Generate h5p related data.
1530        $data = $generator->generate_h5p_data();
1531        // The data of the library 'Library1'.
1532        $library1 = $data->lib1->data;
1533
1534        // Get the dependencies of 'Library1'.
1535        $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1536        // The 'Library1' should have 3 dependencies ('Library2', 'Library3', 'Library4').
1537        $this->assertCount(3, $dependencies);
1538
1539        // Delete the dependencies of 'Library1'.
1540        $this->framework->deleteLibraryDependencies($library1->id);
1541
1542        $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1543        // The 'Library1' should have 0 dependencies.
1544        $this->assertCount(0, $dependencies);
1545    }
1546
1547    /**
1548     * Test the behaviour of deleteLibraryDependencies() when requesting to delete the
1549     * dependencies of a non-existent library.
1550     */
1551    public function test_deleteLibraryDependencies_non_existent_library() {
1552        global $DB;
1553
1554        $this->resetAfterTest();
1555
1556        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1557
1558        // Generate h5p related data.
1559        $data = $generator->generate_h5p_data();
1560        // The data of the library 'Library1'.
1561        $library1 = $data->lib1->data;
1562
1563        // Get the dependencies of 'Library1'.
1564        $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1565        // The 'Library1' should have 3 dependencies ('Library2', 'Library3', 'Library4').
1566        $this->assertCount(3, $dependencies);
1567
1568        // Delete the dependencies of a non-existent library.
1569        $this->framework->deleteLibraryDependencies(0);
1570
1571        $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1572        // The 'Library1' should have 3 dependencies.
1573        $this->assertCount(3, $dependencies);
1574    }
1575
1576    /**
1577     * Test the behaviour of deleteLibrary().
1578     */
1579    public function test_deleteLibrary() {
1580        global $DB;
1581
1582        $this->resetAfterTest();
1583
1584        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1585
1586        // Generate h5p related data.
1587        $data = $generator->generate_h5p_data(true);
1588        // The data of the 'Library1' library.
1589        $library1 = $data->lib1->data;
1590
1591        // Get the library dependencies of 'Library1'.
1592        $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1593
1594        // The 'Library1' should have 3 library dependencies ('Library2', 'Library3', 'Library4').
1595        $this->assertCount(3, $dependencies);
1596
1597        // Return the created 'Library1' files.
1598        $libraryfiles = $DB->get_records('files',
1599            array(
1600                'component' => \core_h5p\file_storage::COMPONENT,
1601                'filearea' => \core_h5p\file_storage::LIBRARY_FILEAREA,
1602                'itemid' => $library1->id
1603            )
1604        );
1605
1606        // The library ('Library1') should have 7 related folders/files.
1607        $this->assertCount(7, $libraryfiles);
1608
1609        // Delete the library.
1610        $this->framework->deleteLibrary($library1);
1611
1612        $lib1 = $DB->get_record('h5p_libraries', ['machinename' => $library1->machinename]);
1613        $dependencies = $DB->get_records('h5p_library_dependencies', ['libraryid' => $library1->id]);
1614        $libraryfiles = $DB->get_records('files',
1615            array(
1616                'component' => \core_h5p\file_storage::COMPONENT,
1617                'filearea' => \core_h5p\file_storage::LIBRARY_FILEAREA,
1618                'itemid' => $library1->id
1619            )
1620        );
1621
1622        // The 'Library1' should not exist.
1623        $this->assertEmpty($lib1);
1624        // The library ('Library1')  should have 0 dependencies.
1625        $this->assertCount(0, $dependencies);
1626        // The library (library1) should have 0 related folders/files.
1627        $this->assertCount(0, $libraryfiles);
1628    }
1629
1630    /**
1631     * Test the behaviour of loadContent().
1632     */
1633    public function test_loadContent() {
1634        global $DB;
1635
1636        $this->resetAfterTest();
1637
1638        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1639
1640        // Generate h5p related data.
1641        $data = $generator->generate_h5p_data();
1642        // The Id of the created h5p content.
1643        $h5pid = $data->h5pcontent->h5pid;
1644        // Get the h5p content data from the DB.
1645        $h5p = $DB->get_record('h5p', ['id' => $h5pid]);
1646        // The data of content's main library ('MainLibrary').
1647        $mainlibrary = $data->mainlib->data;
1648
1649        // Load the h5p content.
1650        $content = $this->framework->loadContent($h5pid);
1651
1652        $expected = array(
1653            'id' => $h5p->id,
1654            'params' => $h5p->jsoncontent,
1655            'embedType' => 'iframe',
1656            'disable' => $h5p->displayoptions,
1657            'title' => $mainlibrary->title,
1658            'slug' => H5PCore::slugify($mainlibrary->title) . '-' . $h5p->id,
1659            'filtered' => $h5p->filtered,
1660            'libraryId' => $mainlibrary->id,
1661            'libraryName' => $mainlibrary->machinename,
1662            'libraryMajorVersion' => $mainlibrary->majorversion,
1663            'libraryMinorVersion' => $mainlibrary->minorversion,
1664            'libraryEmbedTypes' => $mainlibrary->embedtypes,
1665            'libraryFullscreen' => $mainlibrary->fullscreen,
1666            'metadata' => '',
1667            'pathnamehash' => $h5p->pathnamehash
1668        );
1669
1670        $params = json_decode($h5p->jsoncontent);
1671        if (empty($params->metadata)) {
1672            $params->metadata = new \stdClass();
1673        }
1674        $expected['metadata'] = $params->metadata;
1675        $expected['params'] = json_encode($params->params ?? $params);
1676
1677        // The returned content should match the expected array.
1678        $this->assertEquals($expected, $content);
1679    }
1680
1681    /**
1682     * Test the behaviour of loadContentDependencies() when requesting content dependencies
1683     * without specifying the dependency type.
1684     */
1685    public function test_loadContentDependencies_no_type_defined() {
1686        $this->resetAfterTest();
1687
1688        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1689
1690        // Generate h5p related data.
1691        $data = $generator->generate_h5p_data();
1692        // The Id of the h5p content.
1693        $h5pid = $data->h5pcontent->h5pid;
1694        // The content dependencies.
1695        $dependencies = $data->h5pcontent->contentdependencies;
1696
1697        // Add Library5 as a content dependency (dynamic dependency type).
1698        $library5 = $data->lib5->data;
1699        $generator->create_contents_libraries_record($h5pid, $library5->id, 'dynamic');
1700
1701        // Get all content dependencies.
1702        $contentdependencies = $this->framework->loadContentDependencies($h5pid);
1703
1704        $expected = array();
1705        foreach ($dependencies as $dependency) {
1706            $expected[$dependency->machinename] = array(
1707                'libraryId' => $dependency->id,
1708                'machineName' => $dependency->machinename,
1709                'majorVersion' => $dependency->majorversion,
1710                'minorVersion' => $dependency->minorversion,
1711                'patchVersion' => $dependency->patchversion,
1712                'preloadedCss' => $dependency->preloadedcss,
1713                'preloadedJs' => $dependency->preloadedjs,
1714                'dropCss' => '0',
1715                'dependencyType' => 'preloaded'
1716            );
1717        }
1718
1719        $expected = array_merge($expected,
1720            array(
1721                'Library5' => array(
1722                    'libraryId' => $library5->id,
1723                    'machineName' => $library5->machinename,
1724                    'majorVersion' => $library5->majorversion,
1725                    'minorVersion' => $library5->minorversion,
1726                    'patchVersion' => $library5->patchversion,
1727                    'preloadedCss' => $library5->preloadedcss,
1728                    'preloadedJs' => $library5->preloadedjs,
1729                    'dropCss' => '0',
1730                    'dependencyType' => 'dynamic'
1731                )
1732            )
1733        );
1734
1735        // The loaded content dependencies should return 6 libraries.
1736        $this->assertCount(6, $contentdependencies);
1737        $this->assertEquals($expected, $contentdependencies);
1738    }
1739
1740    /**
1741     * Test the behaviour of loadContentDependencies() when requesting content dependencies
1742     * with specifying the dependency type.
1743     */
1744    public function test_loadContentDependencies_type_defined() {
1745        $this->resetAfterTest();
1746
1747        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1748
1749        // Generate h5p related data.
1750        $data = $generator->generate_h5p_data();
1751        // The Id of the h5p content.
1752        $h5pid = $data->h5pcontent->h5pid;
1753        // The content dependencies.
1754        $dependencies = $data->h5pcontent->contentdependencies;
1755
1756        // Add Library5 as a content dependency (dynamic dependency type).
1757        $library5 = $data->lib5->data;
1758        $generator->create_contents_libraries_record($h5pid, $library5->id, 'dynamic');
1759
1760        // Load all content dependencies of dependency type 'preloaded'.
1761        $preloadeddependencies = $this->framework->loadContentDependencies($h5pid, 'preloaded');
1762
1763        $expected = array();
1764        foreach ($dependencies as $dependency) {
1765            $expected[$dependency->machinename] = array(
1766                'libraryId' => $dependency->id,
1767                'machineName' => $dependency->machinename,
1768                'majorVersion' => $dependency->majorversion,
1769                'minorVersion' => $dependency->minorversion,
1770                'patchVersion' => $dependency->patchversion,
1771                'preloadedCss' => $dependency->preloadedcss,
1772                'preloadedJs' => $dependency->preloadedjs,
1773                'dropCss' => '0',
1774                'dependencyType' => 'preloaded'
1775            );
1776        }
1777
1778        // The loaded content dependencies should return 5 libraries.
1779        $this->assertCount(5, $preloadeddependencies);
1780        $this->assertEquals($expected, $preloadeddependencies);
1781
1782        // Load all content dependencies of dependency type 'dynamic'.
1783        $dynamicdependencies = $this->framework->loadContentDependencies($h5pid, 'dynamic');
1784
1785        $expected = array(
1786            'Library5' => array(
1787                'libraryId' => $library5->id,
1788                'machineName' => $library5->machinename,
1789                'majorVersion' => $library5->majorversion,
1790                'minorVersion' => $library5->minorversion,
1791                'patchVersion' => $library5->patchversion,
1792                'preloadedCss' => $library5->preloadedcss,
1793                'preloadedJs' => $library5->preloadedjs,
1794                'dropCss' => '0',
1795                'dependencyType' => 'dynamic'
1796            )
1797        );
1798
1799        // The loaded content dependencies should now return 1 library.
1800        $this->assertCount(1, $dynamicdependencies);
1801        $this->assertEquals($expected, $dynamicdependencies);
1802    }
1803
1804    /**
1805     * Test the behaviour of getOption().
1806     */
1807    public function test_getOption(): void {
1808        $this->resetAfterTest();
1809
1810        // Get value for display_option_download.
1811        $value = $this->framework->getOption(H5PCore::DISPLAY_OPTION_DOWNLOAD);
1812        $expected = H5PDisplayOptionBehaviour::CONTROLLED_BY_AUTHOR_DEFAULT_OFF;
1813        $this->assertEquals($expected, $value);
1814
1815        // Get value for display_option_embed using default value (it should be ignored).
1816        $value = $this->framework->getOption(H5PCore::DISPLAY_OPTION_EMBED, H5PDisplayOptionBehaviour::NEVER_SHOW);
1817        $expected = H5PDisplayOptionBehaviour::CONTROLLED_BY_AUTHOR_DEFAULT_OFF;
1818        $this->assertEquals($expected, $value);
1819
1820        // Get value for unexisting setting without default.
1821        $value = $this->framework->getOption('unexistingsetting');
1822        $expected = false;
1823        $this->assertEquals($expected, $value);
1824
1825        // Get value for unexisting setting with default.
1826        $value = $this->framework->getOption('unexistingsetting', 'defaultvalue');
1827        $expected = 'defaultvalue';
1828        $this->assertEquals($expected, $value);
1829    }
1830
1831    /**
1832     * Test the behaviour of setOption().
1833     */
1834    public function test_setOption(): void {
1835        $this->resetAfterTest();
1836
1837        // Set value for 'newsetting' setting.
1838        $name = 'newsetting';
1839        $value = $this->framework->getOption($name);
1840        $this->assertEquals(false, $value);
1841        $newvalue = 'value1';
1842        $this->framework->setOption($name, $newvalue);
1843        $value = $this->framework->getOption($name);
1844        $this->assertEquals($newvalue, $value);
1845
1846        // Set value for display_option_download and then get it again. Check it hasn't changed.
1847        $name = H5PCore::DISPLAY_OPTION_DOWNLOAD;
1848        $newvalue = H5PDisplayOptionBehaviour::NEVER_SHOW;
1849        $this->framework->setOption($name, $newvalue);
1850        $value = $this->framework->getOption($name);
1851        $expected = H5PDisplayOptionBehaviour::CONTROLLED_BY_AUTHOR_DEFAULT_OFF;
1852        $this->assertEquals($expected, $value);
1853    }
1854
1855    /**
1856     * Test the behaviour of updateContentFields().
1857     */
1858    public function test_updateContentFields() {
1859        global $DB;
1860
1861        $this->resetAfterTest();
1862
1863        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1864
1865        // Create 'Library1' library.
1866        $library1 = $generator->create_library_record('Library1', 'Lib1', 1, 1, 2);
1867        // Create 'Library2' library.
1868        $library2 = $generator->create_library_record('Library2', 'Lib2', 1, 1, 2);
1869
1870        // Create an h5p content with 'Library1' as it's main library.
1871        $h5pid = $generator->create_h5p_record($library1->id, 'iframe');
1872
1873        $updatedata = array(
1874            'jsoncontent' => json_encode(['value' => 'test']),
1875            'mainlibraryid' => $library2->id
1876        );
1877
1878        // Update h5p content fields.
1879        $this->framework->updateContentFields($h5pid, $updatedata);
1880
1881        // Get the h5p content from the DB.
1882        $h5p = $DB->get_record('h5p', ['id' => $h5pid]);
1883
1884        $expected = json_encode(['value' => 'test']);
1885
1886        // Make sure the h5p content fields are properly updated.
1887        $this->assertEquals($expected, $h5p->jsoncontent);
1888        $this->assertEquals($library2->id, $h5p->mainlibraryid);
1889    }
1890
1891    /**
1892     * Test the behaviour of clearFilteredParameters().
1893     */
1894    public function test_clearFilteredParameters() {
1895        global $DB;
1896
1897        $this->resetAfterTest();
1898
1899        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1900
1901        // Create 3 libraries.
1902        $library1 = $generator->create_library_record('Library1', 'Lib1', 1, 1, 2);
1903        $library2 = $generator->create_library_record('Library2', 'Lib2', 1, 1, 2);
1904        $library3 = $generator->create_library_record('Library3', 'Lib3', 1, 1, 2);
1905
1906        // Create h5p content with 'Library1' as a main library.
1907        $h5pcontentid1 = $generator->create_h5p_record($library1->id);
1908        // Create h5p content with 'Library1' as a main library.
1909        $h5pcontentid2 = $generator->create_h5p_record($library1->id);
1910        // Create h5p content with 'Library2' as a main library.
1911        $h5pcontentid3 = $generator->create_h5p_record($library2->id);
1912        // Create h5p content with 'Library3' as a main library.
1913        $h5pcontentid4 = $generator->create_h5p_record($library3->id);
1914
1915        $h5pcontent1 = $DB->get_record('h5p', ['id' => $h5pcontentid1]);
1916        $h5pcontent2 = $DB->get_record('h5p', ['id' => $h5pcontentid2]);
1917        $h5pcontent3 = $DB->get_record('h5p', ['id' => $h5pcontentid3]);
1918        $h5pcontent4 = $DB->get_record('h5p', ['id' => $h5pcontentid4]);
1919
1920        // The filtered parameters should be present in each h5p content.
1921        $this->assertNotEmpty($h5pcontent1->filtered);
1922        $this->assertNotEmpty($h5pcontent2->filtered);
1923        $this->assertNotEmpty($h5pcontent3->filtered);
1924        $this->assertNotEmpty($h5pcontent4->filtered);
1925
1926        // Clear the filtered parameters for contents that have library1 and library3 as
1927        // their main library.
1928        $this->framework->clearFilteredParameters([$library1->id, $library3->id]);
1929
1930        $h5pcontent1 = $DB->get_record('h5p', ['id' => $h5pcontentid1]);
1931        $h5pcontent2 = $DB->get_record('h5p', ['id' => $h5pcontentid2]);
1932        $h5pcontent3 = $DB->get_record('h5p', ['id' => $h5pcontentid3]);
1933        $h5pcontent4 = $DB->get_record('h5p', ['id' => $h5pcontentid4]);
1934
1935        // The filtered parameters should be still present only for the content that has
1936        // library 2 as a main library.
1937        $this->assertEmpty($h5pcontent1->filtered);
1938        $this->assertEmpty($h5pcontent2->filtered);
1939        $this->assertNotEmpty($h5pcontent3->filtered);
1940        $this->assertEmpty($h5pcontent4->filtered);
1941    }
1942
1943    /**
1944     * Test the behaviour of getNumNotFiltered().
1945     */
1946    public function test_getNumNotFiltered() {
1947        global $DB;
1948
1949        $this->resetAfterTest();
1950
1951        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1952
1953        // Create 3 libraries.
1954        $library1 = $generator->create_library_record('Library1', 'Lib1', 1, 1, 2);
1955        $library2 = $generator->create_library_record('Library2', 'Lib2', 1, 1, 2);
1956        $library3 = $generator->create_library_record('Library3', 'Lib3', 1, 1, 2);
1957
1958        // Create h5p content with library1 as a main library.
1959        $h5pcontentid1 = $generator->create_h5p_record($library1->id);
1960        // Create h5p content with library1 as a main library.
1961        $h5pcontentid2 = $generator->create_h5p_record($library1->id);
1962        // Create h5p content with library2 as a main library.
1963        $h5pcontentid3 = $generator->create_h5p_record($library2->id);
1964        // Create h5p content with library3 as a main library.
1965        $h5pcontentid4 = $generator->create_h5p_record($library3->id);
1966
1967        $h5pcontent1 = $DB->get_record('h5p', ['id' => $h5pcontentid1]);
1968        $h5pcontent2 = $DB->get_record('h5p', ['id' => $h5pcontentid2]);
1969        $h5pcontent3 = $DB->get_record('h5p', ['id' => $h5pcontentid3]);
1970        $h5pcontent4 = $DB->get_record('h5p', ['id' => $h5pcontentid4]);
1971
1972        // The filtered parameters should be present in each h5p content.
1973        $this->assertNotEmpty($h5pcontent1->filtered);
1974        $this->assertNotEmpty($h5pcontent2->filtered);
1975        $this->assertNotEmpty($h5pcontent3->filtered);
1976        $this->assertNotEmpty($h5pcontent4->filtered);
1977
1978        // Clear the filtered parameters for contents that have library1 and library3 as
1979        // their main library.
1980        $this->framework->clearFilteredParameters([$library1->id, $library3->id]);
1981
1982        $countnotfiltered = $this->framework->getNumNotFiltered();
1983
1984        // 3 contents don't have their parameters filtered.
1985        $this->assertEquals(3, $countnotfiltered);
1986    }
1987
1988    /**
1989     * Test the behaviour of getNumContent().
1990     */
1991    public function test_getNumContent() {
1992        $this->resetAfterTest();
1993
1994        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
1995
1996        // Generate h5p related data.
1997        $data = $generator->generate_h5p_data();
1998
1999        // The 'MainLibrary' library data.
2000        $mainlibrary = $data->mainlib->data;
2001
2002        // The 'Library1' library data.
2003        $library1 = $data->lib1->data;
2004
2005        // Create new h5p content with MainLibrary as a main library.
2006        $generator->create_h5p_record($mainlibrary->id);
2007
2008        // Get the number of h5p contents that are using 'MainLibrary' as their main library.
2009        $countmainlib = $this->framework->getNumContent($mainlibrary->id);
2010
2011        // Get the number of h5p contents that are using 'Library1' as their main library.
2012        $countlib1 = $this->framework->getNumContent($library1->id);
2013
2014        // Make sure that 2 contents are using MainLibrary as their main library.
2015        $this->assertEquals(2, $countmainlib);
2016        // Make sure that 0 contents are using Library1 as their main library.
2017        $this->assertEquals(0, $countlib1);
2018    }
2019
2020    /**
2021     * Test the behaviour of getNumContent() when certain contents are being skipped.
2022     */
2023    public function test_getNumContent_skip_content() {
2024        $this->resetAfterTest();
2025
2026        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
2027
2028        // Generate h5p related data.
2029        $data = $generator->generate_h5p_data();
2030
2031        // The 'MainLibrary' library data.
2032        $mainlibrary = $data->mainlib->data;
2033
2034        // Create new h5p content with MainLibrary as a main library.
2035        $h5pcontentid = $generator->create_h5p_record($mainlibrary->id);
2036
2037        // Get the number of h5p contents that are using 'MainLibrary' as their main library.
2038        // Skip the newly created content $h5pcontentid.
2039        $countmainlib = $this->framework->getNumContent($mainlibrary->id, [$h5pcontentid]);
2040
2041        // Make sure that 1 content is returned instead of 2 ($h5pcontentid being skipped).
2042        $this->assertEquals(1, $countmainlib);
2043    }
2044
2045    /**
2046     * Test the behaviour of isContentSlugAvailable().
2047     */
2048    public function test_isContentSlugAvailable() {
2049        $this->resetAfterTest();
2050
2051        $slug = 'h5p-test-slug-1';
2052
2053        // Currently this returns always true. The slug is generated as a unique value for
2054        // each h5p content and it is not stored in the h5p content table.
2055        $isslugavailable = $this->framework->isContentSlugAvailable($slug);
2056
2057        $this->assertTrue($isslugavailable);
2058    }
2059
2060    /**
2061     * Test that a record is stored for cached assets.
2062     */
2063    public function test_saveCachedAssets() {
2064        global $DB;
2065
2066        $this->resetAfterTest();
2067
2068        $libraries = array(
2069            array(
2070                'machineName' => 'H5P.TestLib',
2071                'libraryId' => 405,
2072            ),
2073            array(
2074                'FontAwesome' => 'FontAwesome',
2075                'libraryId' => 406,
2076            ),
2077            array(
2078                'machineName' => 'H5P.SecondLib',
2079                'libraryId' => 407,
2080            ),
2081        );
2082
2083        $key = 'testhashkey';
2084
2085        $this->framework->saveCachedAssets($key, $libraries);
2086
2087        $records = $DB->get_records('h5p_libraries_cachedassets');
2088
2089        $this->assertCount(3, $records);
2090    }
2091
2092    /**
2093     * Test that the correct libraries are removed from the cached assets table
2094     */
2095    public function test_deleteCachedAssets() {
2096        global $DB;
2097
2098        $this->resetAfterTest();
2099
2100        $libraries = array(
2101            array(
2102                'machineName' => 'H5P.TestLib',
2103                'libraryId' => 405,
2104            ),
2105            array(
2106                'FontAwesome' => 'FontAwesome',
2107                'libraryId' => 406,
2108            ),
2109            array(
2110                'machineName' => 'H5P.SecondLib',
2111                'libraryId' => 407,
2112            ),
2113        );
2114
2115        $key1 = 'testhashkey';
2116        $this->framework->saveCachedAssets($key1, $libraries);
2117
2118        $libraries = array(
2119            array(
2120                'machineName' => 'H5P.DiffLib',
2121                'libraryId' => 408,
2122            ),
2123            array(
2124                'FontAwesome' => 'FontAwesome',
2125                'libraryId' => 406,
2126            ),
2127            array(
2128                'machineName' => 'H5P.ThirdLib',
2129                'libraryId' => 409,
2130            ),
2131        );
2132
2133        $key2 = 'secondhashkey';
2134        $this->framework->saveCachedAssets($key2, $libraries);
2135
2136        $libraries = array(
2137            array(
2138                'machineName' => 'H5P.AnotherDiffLib',
2139                'libraryId' => 410,
2140            ),
2141            array(
2142                'FontAwesome' => 'NotRelated',
2143                'libraryId' => 411,
2144            ),
2145            array(
2146                'machineName' => 'H5P.ForthLib',
2147                'libraryId' => 412,
2148            ),
2149        );
2150
2151        $key3 = 'threeforthewin';
2152        $this->framework->saveCachedAssets($key3, $libraries);
2153
2154        $records = $DB->get_records('h5p_libraries_cachedassets');
2155        $this->assertCount(9, $records);
2156
2157        // Selecting one library id will result in all related library entries also being deleted.
2158        // Going to use the FontAwesome library id. The first two hashes should be returned.
2159        $hashes = $this->framework->deleteCachedAssets(406);
2160        $this->assertCount(2, $hashes);
2161        $index = array_search($key1, $hashes);
2162        $this->assertEquals($key1, $hashes[$index]);
2163        $index = array_search($key2, $hashes);
2164        $this->assertEquals($key2, $hashes[$index]);
2165        $index = array_search($key3, $hashes);
2166        $this->assertFalse($index);
2167
2168        // Check that the records have been removed as well.
2169        $records = $DB->get_records('h5p_libraries_cachedassets');
2170        $this->assertCount(3, $records);
2171    }
2172
2173    /**
2174     * Test the behaviour of getLibraryContentCount().
2175     */
2176    public function test_getLibraryContentCount() {
2177        $this->resetAfterTest();
2178
2179        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
2180
2181        // Generate h5p related data.
2182        $data = $generator->generate_h5p_data();
2183
2184        // The 'MainLibrary' library data.
2185        $mainlibrary = $data->mainlib->data;
2186
2187        // The 'Library2' library data.
2188        $library2 = $data->lib2->data;
2189
2190        // Create new h5p content with Library2 as it's main library.
2191        $generator->create_h5p_record($library2->id);
2192
2193        // Create new h5p content with MainLibrary as it's main library.
2194        $generator->create_h5p_record($mainlibrary->id);
2195
2196        $countlibrarycontent = $this->framework->getLibraryContentCount();
2197
2198        $expected = array(
2199            "{$mainlibrary->machinename} {$mainlibrary->majorversion}.{$mainlibrary->minorversion}" => 2,
2200            "{$library2->machinename} {$library2->majorversion}.{$library2->minorversion}" => 1,
2201        );
2202
2203        // MainLibrary and Library1 are currently main libraries to the existing h5p contents.
2204        // Should return the number of cases where MainLibrary and Library1 are main libraries to an h5p content.
2205        $this->assertEquals($expected, $countlibrarycontent);
2206    }
2207
2208    /**
2209     * Test the behaviour of test_libraryHasUpgrade().
2210     *
2211     * @dataProvider test_libraryHasUpgrade_provider
2212     * @param array $libraryrecords Array containing data for the library creation
2213     * @param array $testlibrary Array containing the test library data
2214     * @param bool $expected The expectation whether the library is patched or not
2215     **/
2216    public function test_libraryHasUpgrade(array $libraryrecords, array $testlibrary, bool $expected): void {
2217        $this->resetAfterTest();
2218
2219        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
2220
2221        foreach ($libraryrecords as $library) {
2222            call_user_func_array([$generator, 'create_library_record'], $library);
2223        }
2224
2225        $this->assertEquals($expected, $this->framework->libraryHasUpgrade($testlibrary));
2226    }
2227
2228    /**
2229     * Data provider for test_libraryHasUpgrade().
2230     *
2231     * @return array
2232     */
2233    public function test_libraryHasUpgrade_provider(): array {
2234        return [
2235            'Lower major version; Identical lower version' => [
2236                [
2237                    ['Library', 'Lib', 2, 2],
2238                ],
2239                [
2240                    'machineName' => 'Library',
2241                    'majorVersion' => 1,
2242                    'minorVersion' => 2
2243                ],
2244                true,
2245            ],
2246            'Major version identical; Lower minor version' => [
2247                [
2248                    ['Library', 'Lib', 2, 2],
2249                ],
2250                [
2251                    'machineName' => 'Library',
2252                    'majorVersion' => 2,
2253                    'minorVersion' => 1
2254                ],
2255                true,
2256            ],
2257            'Major version identical; Minor version identical' => [
2258                [
2259                    ['Library', 'Lib', 2, 2],
2260                ],
2261                [
2262                    'machineName' => 'Library',
2263                    'majorVersion' => 2,
2264                    'minorVersion' => 2
2265                ],
2266                false,
2267            ],
2268            'Major version higher; Minor version identical' => [
2269                [
2270                    ['Library', 'Lib', 2, 2],
2271                ],
2272                [
2273                    'machineName' => 'Library',
2274                    'majorVersion' => 3,
2275                    'minorVersion' => 2
2276                ],
2277                false,
2278            ],
2279            'Major version identical; Minor version newer' => [
2280                [
2281                    ['Library', 'Lib', 2, 2],
2282                ],
2283                [
2284                    'machineName' => 'Library',
2285                    'majorVersion' => 2,
2286                    'minorVersion' => 4
2287                ],
2288                false,
2289            ]
2290        ];
2291    }
2292
2293
2294    /**
2295     * Test the behaviour of get_latest_library_version().
2296     */
2297    public function test_get_latest_library_version() {
2298        global $DB;
2299
2300        $this->resetAfterTest();
2301
2302        $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
2303        // Create a library record.
2304        $machinename = 'TestLibrary';
2305        $lib1 = $generator->create_library_record($machinename, 'Test', 1, 1, 2);
2306        $lib2 = $generator->create_library_record($machinename, 'Test', 1, 2, 1);
2307
2308        $content = array(
2309            'params' => json_encode(['param1' => 'Test']),
2310            'library' => array(
2311                'libraryId' => 0,
2312                'machineName' => 'TestLibrary',
2313            ),
2314            'disable' => 8
2315        );
2316
2317        // Get the latest id (at this point, should be lib2).
2318        $latestlib = $this->framework->get_latest_library_version($machinename);
2319        $this->assertEquals($lib2->id, $latestlib->id);
2320
2321        // Get the latest id (at this point, should be lib3).
2322        $lib3 = $generator->create_library_record($machinename, 'Test', 2, 1, 0);
2323        $latestlib = $this->framework->get_latest_library_version($machinename);
2324        $this->assertEquals($lib3->id, $latestlib->id);
2325
2326        // Get the latest id (at this point, should be still lib3).
2327        $lib4 = $generator->create_library_record($machinename, 'Test', 1, 1, 3);
2328        $latestlib = $this->framework->get_latest_library_version($machinename);
2329        $this->assertEquals($lib3->id, $latestlib->id);
2330
2331        // Get the latest id (at this point, should be lib5).
2332        $lib5 = $generator->create_library_record($machinename, 'Test', 2, 1, 6);
2333        $latestlib = $this->framework->get_latest_library_version($machinename);
2334        $this->assertEquals($lib5->id, $latestlib->id);
2335    }
2336}
2337