2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Unit Tests for the Moodle Content Writer.
20 * @package core_privacy
22 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
30 use \core_privacy\local\request\writer
;
31 use \core_privacy\local\request\moodle_content_writer
;
34 * Tests for the \core_privacy API's moodle_content_writer functionality.
36 * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
37 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 class moodle_content_writer_test
extends advanced_testcase
{
42 * Test that exported data is saved correctly within the system context.
44 * @dataProvider export_data_provider
45 * @param \stdClass $data Data
47 public function test_export_data($data) {
48 $context = \context_system
::instance();
51 $writer = $this->get_writer_instance()
52 ->set_context($context)
53 ->export_data($subcontext, $data);
55 $fileroot = $this->fetch_exported_content($writer);
57 $contextpath = $this->get_context_path($context, $subcontext, 'data.json');
58 $this->assertTrue($fileroot->hasChild($contextpath));
60 $json = $fileroot->getChild($contextpath)->getContent();
61 $expanded = json_decode($json);
62 $this->assertEquals($data, $expanded);
66 * Test that exported data is saved correctly for context/subcontext.
68 * @dataProvider export_data_provider
69 * @param \stdClass $data Data
71 public function test_export_data_different_context($data) {
72 $context = \context_user
::instance(\core_user
::get_user_by_username('admin')->id
);
73 $subcontext = ['sub', 'context'];
75 $writer = $this->get_writer_instance()
76 ->set_context($context)
77 ->export_data($subcontext, $data);
79 $fileroot = $this->fetch_exported_content($writer);
81 $contextpath = $this->get_context_path($context, $subcontext, 'data.json');
82 $this->assertTrue($fileroot->hasChild($contextpath));
84 $json = $fileroot->getChild($contextpath)->getContent();
85 $expanded = json_decode($json);
86 $this->assertEquals($data, $expanded);
90 * Test that exported is saved within the correct directory locations.
92 public function test_export_data_writes_to_multiple_context() {
93 $subcontext = ['sub', 'context'];
95 $systemcontext = \context_system
::instance();
96 $systemdata = (object) [
97 'belongsto' => 'system',
99 $usercontext = \context_user
::instance(\core_user
::get_user_by_username('admin')->id
);
100 $userdata = (object) [
101 'belongsto' => 'user',
104 $writer = $this->get_writer_instance();
107 ->set_context($systemcontext)
108 ->export_data($subcontext, $systemdata);
111 ->set_context($usercontext)
112 ->export_data($subcontext, $userdata);
114 $fileroot = $this->fetch_exported_content($writer);
116 $contextpath = $this->get_context_path($systemcontext, $subcontext, 'data.json');
117 $this->assertTrue($fileroot->hasChild($contextpath));
119 $json = $fileroot->getChild($contextpath)->getContent();
120 $expanded = json_decode($json);
121 $this->assertEquals($systemdata, $expanded);
123 $contextpath = $this->get_context_path($usercontext, $subcontext, 'data.json');
124 $this->assertTrue($fileroot->hasChild($contextpath));
126 $json = $fileroot->getChild($contextpath)->getContent();
127 $expanded = json_decode($json);
128 $this->assertEquals($userdata, $expanded);
132 * Test that multiple writes to the same location cause the latest version to be written.
134 public function test_export_data_multiple_writes_same_context() {
135 $subcontext = ['sub', 'context'];
137 $systemcontext = \context_system
::instance();
138 $originaldata = (object) [
139 'belongsto' => 'system',
142 $newdata = (object) [
146 $writer = $this->get_writer_instance();
149 ->set_context($systemcontext)
150 ->export_data($subcontext, $originaldata);
153 ->set_context($systemcontext)
154 ->export_data($subcontext, $newdata);
156 $fileroot = $this->fetch_exported_content($writer);
158 $contextpath = $this->get_context_path($systemcontext, $subcontext, 'data.json');
159 $this->assertTrue($fileroot->hasChild($contextpath));
161 $json = $fileroot->getChild($contextpath)->getContent();
162 $expanded = json_decode($json);
163 $this->assertEquals($newdata, $expanded);
167 * Data provider for exporting user data.
169 public function export_data_provider() {
173 'example' => (object) [
182 * Test that metadata can be set.
184 * @dataProvider export_metadata_provider
185 * @param string $key Key
186 * @param string $value Value
187 * @param string $description Description
189 public function test_export_metadata($key, $value, $description) {
190 $context = \context_system
::instance();
191 $subcontext = ['a', 'b', 'c'];
193 $writer = $this->get_writer_instance()
194 ->set_context($context)
195 ->export_metadata($subcontext, $key, $value, $description);
197 $fileroot = $this->fetch_exported_content($writer);
199 $contextpath = $this->get_context_path($context, $subcontext, 'metadata.json');
200 $this->assertTrue($fileroot->hasChild($contextpath));
202 $json = $fileroot->getChild($contextpath)->getContent();
203 $expanded = json_decode($json);
204 $this->assertTrue(isset($expanded->$key));
205 $this->assertEquals($value, $expanded->$key->value
);
206 $this->assertEquals($description, $expanded->$key->description
);
210 * Test that metadata can be set additively.
212 public function test_export_metadata_additive() {
213 $context = \context_system
::instance();
216 $writer = $this->get_writer_instance();
219 ->set_context($context)
220 ->export_metadata($subcontext, 'firstkey', 'firstvalue', 'firstdescription');
223 ->set_context($context)
224 ->export_metadata($subcontext, 'secondkey', 'secondvalue', 'seconddescription');
226 $fileroot = $this->fetch_exported_content($writer);
228 $contextpath = $this->get_context_path($context, $subcontext, 'metadata.json');
229 $this->assertTrue($fileroot->hasChild($contextpath));
231 $json = $fileroot->getChild($contextpath)->getContent();
232 $expanded = json_decode($json);
234 $this->assertTrue(isset($expanded->firstkey
));
235 $this->assertEquals('firstvalue', $expanded->firstkey
->value
);
236 $this->assertEquals('firstdescription', $expanded->firstkey
->description
);
238 $this->assertTrue(isset($expanded->secondkey
));
239 $this->assertEquals('secondvalue', $expanded->secondkey
->value
);
240 $this->assertEquals('seconddescription', $expanded->secondkey
->description
);
244 * Test that metadata can be set additively.
246 public function test_export_metadata_to_multiple_contexts() {
247 $systemcontext = \context_system
::instance();
248 $usercontext = \context_user
::instance(\core_user
::get_user_by_username('admin')->id
);
251 $writer = $this->get_writer_instance();
254 ->set_context($systemcontext)
255 ->export_metadata($subcontext, 'firstkey', 'firstvalue', 'firstdescription')
256 ->export_metadata($subcontext, 'secondkey', 'secondvalue', 'seconddescription');
259 ->set_context($usercontext)
260 ->export_metadata($subcontext, 'firstkey', 'alternativevalue', 'alternativedescription')
261 ->export_metadata($subcontext, 'thirdkey', 'thirdvalue', 'thirddescription');
263 $fileroot = $this->fetch_exported_content($writer);
265 $systemcontextpath = $this->get_context_path($systemcontext, $subcontext, 'metadata.json');
266 $this->assertTrue($fileroot->hasChild($systemcontextpath));
268 $json = $fileroot->getChild($systemcontextpath)->getContent();
269 $expanded = json_decode($json);
271 $this->assertTrue(isset($expanded->firstkey
));
272 $this->assertEquals('firstvalue', $expanded->firstkey
->value
);
273 $this->assertEquals('firstdescription', $expanded->firstkey
->description
);
274 $this->assertTrue(isset($expanded->secondkey
));
275 $this->assertEquals('secondvalue', $expanded->secondkey
->value
);
276 $this->assertEquals('seconddescription', $expanded->secondkey
->description
);
277 $this->assertFalse(isset($expanded->thirdkey
));
279 $usercontextpath = $this->get_context_path($usercontext, $subcontext, 'metadata.json');
280 $this->assertTrue($fileroot->hasChild($usercontextpath));
282 $json = $fileroot->getChild($usercontextpath)->getContent();
283 $expanded = json_decode($json);
285 $this->assertTrue(isset($expanded->firstkey
));
286 $this->assertEquals('alternativevalue', $expanded->firstkey
->value
);
287 $this->assertEquals('alternativedescription', $expanded->firstkey
->description
);
288 $this->assertFalse(isset($expanded->secondkey
));
289 $this->assertTrue(isset($expanded->thirdkey
));
290 $this->assertEquals('thirdvalue', $expanded->thirdkey
->value
);
291 $this->assertEquals('thirddescription', $expanded->thirdkey
->description
);
295 * Data provider for exporting user metadata.
299 public function export_metadata_provider() {
304 'This is a description',
306 'valuewithspaces' => [
309 'This is a description',
313 base64_encode('value has mixed'),
314 'This is a description',
320 * Exporting a single stored_file should cause that file to be output in the files directory.
322 public function test_export_area_files() {
323 $this->resetAfterTest();
324 $context = \context_system
::instance();
325 $fs = get_file_storage();
327 // Add two files to core_privacy::tests::0.
330 'component' => 'core_privacy',
331 'filearea' => 'tests',
335 'content' => 'Test file 0',
340 'component' => 'core_privacy',
341 'filearea' => 'tests',
345 'content' => 'Test file 1',
349 // One with a different itemid.
351 'component' => 'core_privacy',
352 'filearea' => 'tests',
356 'content' => 'Other',
360 // One with a different filearea.
362 'component' => 'core_privacy',
363 'filearea' => 'alternative',
367 'content' => 'Alternative',
371 // One with a different component.
373 'component' => 'core',
374 'filearea' => 'tests',
378 'content' => 'Other tests',
382 foreach ($files as $file) {
384 'contextid' => $context->id
,
385 'component' => $file->component
,
386 'filearea' => $file->filearea
,
387 'itemid' => $file->itemid
,
388 'filepath' => $file->path
,
389 'filename' => $file->name
,
392 $file->namepath
= '/' . $file->filearea
. '/' . ($file->itemid ?
: '') . $file->path
. $file->name
;
393 $file->storedfile
= $fs->create_file_from_string($record, $file->content
);
396 $writer = $this->get_writer_instance()
397 ->set_context($context)
398 ->export_area_files([], 'core_privacy', 'tests', 0);
400 $fileroot = $this->fetch_exported_content($writer);
402 $firstfiles = array_slice($files, 0, 2);
403 foreach ($firstfiles as $file) {
404 $contextpath = $this->get_context_path($context, ['_files'], $file->namepath
);
405 $this->assertTrue($fileroot->hasChild($contextpath));
406 $this->assertEquals($file->content
, $fileroot->getChild($contextpath)->getContent());
409 $otherfiles = array_slice($files, 2);
410 foreach ($otherfiles as $file) {
411 $contextpath = $this->get_context_path($context, ['_files'], $file->namepath
);
412 $this->assertFalse($fileroot->hasChild($contextpath));
417 * Exporting a single stored_file should cause that file to be output in the files directory.
419 * @dataProvider export_file_provider
420 * @param string $filearea File area
421 * @param int $itemid Item ID
422 * @param string $filepath File path
423 * @param string $filename File name
424 * @param string $content Content
426 public function test_export_file($filearea, $itemid, $filepath, $filename, $content) {
427 $this->resetAfterTest();
428 $context = \context_system
::instance();
429 $filenamepath = '/' . $filearea . '/' . ($itemid ?
: '') . $filepath . $filename;
432 'contextid' => $context->id
,
433 'component' => 'core_privacy',
434 'filearea' => $filearea,
436 'filepath' => $filepath,
437 'filename' => $filename,
440 $fs = get_file_storage();
441 $file = $fs->create_file_from_string($filerecord, $content);
443 $writer = $this->get_writer_instance()
444 ->set_context($context)
445 ->export_file([], $file);
447 $fileroot = $this->fetch_exported_content($writer);
449 $contextpath = $this->get_context_path($context, ['_files'], $filenamepath);
450 $this->assertTrue($fileroot->hasChild($contextpath));
451 $this->assertEquals($content, $fileroot->getChild($contextpath)->getContent());
455 * Data provider for the test_export_file function.
459 public function export_file_provider() {
466 'An example file content',
471 '/path/within/a/path/within/a/path/',
473 'An example file content',
475 'pathwithspaces' => [
478 '/path with/some spaces/',
480 'An example file content',
482 'filewithspaces' => [
483 'submission_attachments',
485 '/path with/some spaces/',
487 'An example file content',
494 file_get_contents(__DIR__
. '/fixtures/logo.png'),
497 'submission_content',
514 * User preferences can be exported against a user.
516 * @dataProvider export_user_preference_provider
517 * @param string $component Component
518 * @param string $key Key
519 * @param string $value Value
520 * @param string $desc Description
522 public function test_export_user_preference_context_user($component, $key, $value, $desc) {
523 $admin = \core_user
::get_user_by_username('admin');
525 $writer = $this->get_writer_instance();
527 $context = \context_user
::instance($admin->id
);
528 $writer = $this->get_writer_instance()
529 ->set_context($context)
530 ->export_user_preference($component, $key, $value, $desc);
532 $fileroot = $this->fetch_exported_content($writer);
534 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
535 $this->assertTrue($fileroot->hasChild($contextpath));
537 $json = $fileroot->getChild($contextpath)->getContent();
538 $expanded = json_decode($json);
539 $this->assertTrue(isset($expanded->$key));
540 $data = $expanded->$key;
541 $this->assertEquals($value, $data->value
);
542 $this->assertEquals($desc, $data->description
);
546 * User preferences can be exported against a course category.
548 * @dataProvider export_user_preference_provider
549 * @param string $component Component
550 * @param string $key Key
551 * @param string $value Value
552 * @param string $desc Description
554 public function test_export_user_preference_context_coursecat($component, $key, $value, $desc) {
557 $categories = $DB->get_records('course_categories');
558 $firstcategory = reset($categories);
560 $context = \context_coursecat
::instance($firstcategory->id
);
561 $writer = $this->get_writer_instance()
562 ->set_context($context)
563 ->export_user_preference($component, $key, $value, $desc);
565 $fileroot = $this->fetch_exported_content($writer);
567 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
568 $this->assertTrue($fileroot->hasChild($contextpath));
570 $json = $fileroot->getChild($contextpath)->getContent();
571 $expanded = json_decode($json);
572 $this->assertTrue(isset($expanded->$key));
573 $data = $expanded->$key;
574 $this->assertEquals($value, $data->value
);
575 $this->assertEquals($desc, $data->description
);
579 * User preferences can be exported against a course.
581 * @dataProvider export_user_preference_provider
582 * @param string $component Component
583 * @param string $key Key
584 * @param string $value Value
585 * @param string $desc Description
587 public function test_export_user_preference_context_course($component, $key, $value, $desc) {
590 $this->resetAfterTest();
592 $course = $this->getDataGenerator()->create_course();
594 $context = \context_course
::instance($course->id
);
595 $writer = $this->get_writer_instance()
596 ->set_context($context)
597 ->export_user_preference($component, $key, $value, $desc);
599 $fileroot = $this->fetch_exported_content($writer);
601 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
602 $this->assertTrue($fileroot->hasChild($contextpath));
604 $json = $fileroot->getChild($contextpath)->getContent();
605 $expanded = json_decode($json);
606 $this->assertTrue(isset($expanded->$key));
607 $data = $expanded->$key;
608 $this->assertEquals($value, $data->value
);
609 $this->assertEquals($desc, $data->description
);
613 * User preferences can be exported against a module context.
615 * @dataProvider export_user_preference_provider
616 * @param string $component Component
617 * @param string $key Key
618 * @param string $value Value
619 * @param string $desc Description
621 public function test_export_user_preference_context_module($component, $key, $value, $desc) {
624 $this->resetAfterTest();
626 $course = $this->getDataGenerator()->create_course();
627 $forum = $this->getDataGenerator()->create_module('forum', ['course' => $course->id
]);
629 $context = \context_module
::instance($forum->cmid
);
630 $writer = $this->get_writer_instance()
631 ->set_context($context)
632 ->export_user_preference($component, $key, $value, $desc);
634 $fileroot = $this->fetch_exported_content($writer);
636 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
637 $this->assertTrue($fileroot->hasChild($contextpath));
639 $json = $fileroot->getChild($contextpath)->getContent();
640 $expanded = json_decode($json);
641 $this->assertTrue(isset($expanded->$key));
642 $data = $expanded->$key;
643 $this->assertEquals($value, $data->value
);
644 $this->assertEquals($desc, $data->description
);
648 * User preferences can not be exported against a block context.
650 * @dataProvider export_user_preference_provider
651 * @param string $component Component
652 * @param string $key Key
653 * @param string $value Value
654 * @param string $desc Description
656 public function test_export_user_preference_context_block($component, $key, $value, $desc) {
659 $blocks = $DB->get_records('block_instances');
660 $block = reset($blocks);
662 $context = \context_block
::instance($block->id
);
663 $writer = $this->get_writer_instance()
664 ->set_context($context)
665 ->export_user_preference($component, $key, $value, $desc);
667 $fileroot = $this->fetch_exported_content($writer);
669 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
670 $this->assertTrue($fileroot->hasChild($contextpath));
672 $json = $fileroot->getChild($contextpath)->getContent();
673 $expanded = json_decode($json);
674 $this->assertTrue(isset($expanded->$key));
675 $data = $expanded->$key;
676 $this->assertEquals($value, $data->value
);
677 $this->assertEquals($desc, $data->description
);
681 * Writing user preferences for two different blocks with the same name and
682 * same parent context should generate two different context paths and export
685 public function test_export_user_preference_context_block_multiple_instances() {
686 $this->resetAfterTest();
688 $generator = $this->getDataGenerator();
689 $course = $generator->create_course();
690 $coursecontext = context_course
::instance($course->id
);
691 $block1 = $generator->create_block('online_users', ['parentcontextid' => $coursecontext->id
]);
692 $block2 = $generator->create_block('online_users', ['parentcontextid' => $coursecontext->id
]);
693 $block1context = context_block
::instance($block1->id
);
694 $block2context = context_block
::instance($block2->id
);
695 $component = 'block';
696 $desc = 'test preference';
697 $block1key = 'block1key';
698 $block1value = 'block1value';
699 $block2key = 'block2key';
700 $block2value = 'block2value';
701 $writer = $this->get_writer_instance();
703 // Confirm that we have two different block contexts with the same name
704 // and the same parent context id.
705 $this->assertNotEquals($block1context->id
, $block2context->id
);
706 $this->assertEquals($block1context->get_context_name(), $block2context->get_context_name());
707 $this->assertEquals($block1context->get_parent_context()->id
, $block2context->get_parent_context()->id
);
709 $retrieveexport = function($context) use ($writer, $component) {
710 $fileroot = $this->fetch_exported_content($writer);
712 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
713 $this->assertTrue($fileroot->hasChild($contextpath));
715 $json = $fileroot->getChild($contextpath)->getContent();
716 return json_decode($json);
719 $writer->set_context($block1context)
720 ->export_user_preference($component, $block1key, $block1value, $desc);
721 $writer->set_context($block2context)
722 ->export_user_preference($component, $block2key, $block2value, $desc);
724 $block1export = $retrieveexport($block1context);
725 $block2export = $retrieveexport($block2context);
727 // Confirm that the exports didn't write to the same file.
728 $this->assertTrue(isset($block1export->$block1key));
729 $this->assertTrue(isset($block2export->$block2key));
730 $this->assertFalse(isset($block1export->$block2key));
731 $this->assertFalse(isset($block2export->$block1key));
732 $this->assertEquals($block1value, $block1export->$block1key->value
);
733 $this->assertEquals($block2value, $block2export->$block2key->value
);
737 * User preferences can be exported against the system.
739 * @dataProvider export_user_preference_provider
740 * @param string $component Component
741 * @param string $key Key
742 * @param string $value Value
743 * @param string $desc Description
745 public function test_export_user_preference_context_system($component, $key, $value, $desc) {
746 $context = \context_system
::instance();
747 $writer = $this->get_writer_instance()
748 ->set_context($context)
749 ->export_user_preference($component, $key, $value, $desc);
751 $fileroot = $this->fetch_exported_content($writer);
753 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
754 $this->assertTrue($fileroot->hasChild($contextpath));
756 $json = $fileroot->getChild($contextpath)->getContent();
757 $expanded = json_decode($json);
758 $this->assertTrue(isset($expanded->$key));
759 $data = $expanded->$key;
760 $this->assertEquals($value, $data->value
);
761 $this->assertEquals($desc, $data->description
);
765 * User preferences can be exported against the system.
767 public function test_export_multiple_user_preference_context_system() {
768 $context = \context_system
::instance();
769 $writer = $this->get_writer_instance();
770 $component = 'core_privacy';
773 ->set_context($context)
774 ->export_user_preference($component, 'key1', 'val1', 'desc1')
775 ->export_user_preference($component, 'key2', 'val2', 'desc2');
777 $fileroot = $this->fetch_exported_content($writer);
779 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
780 $this->assertTrue($fileroot->hasChild($contextpath));
782 $json = $fileroot->getChild($contextpath)->getContent();
783 $expanded = json_decode($json);
785 $this->assertTrue(isset($expanded->key1
));
786 $data = $expanded->key1
;
787 $this->assertEquals('val1', $data->value
);
788 $this->assertEquals('desc1', $data->description
);
790 $this->assertTrue(isset($expanded->key2
));
791 $data = $expanded->key2
;
792 $this->assertEquals('val2', $data->value
);
793 $this->assertEquals('desc2', $data->description
);
797 * User preferences can be exported against the system.
799 public function test_export_user_preference_replace() {
800 $context = \context_system
::instance();
801 $writer = $this->get_writer_instance();
802 $component = 'core_privacy';
806 ->set_context($context)
807 ->export_user_preference($component, $key, 'val1', 'desc1');
810 ->set_context($context)
811 ->export_user_preference($component, $key, 'val2', 'desc2');
813 $fileroot = $this->fetch_exported_content($writer);
815 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
816 $this->assertTrue($fileroot->hasChild($contextpath));
818 $json = $fileroot->getChild($contextpath)->getContent();
819 $expanded = json_decode($json);
821 $this->assertTrue(isset($expanded->$key));
822 $data = $expanded->$key;
823 $this->assertEquals('val2', $data->value
);
824 $this->assertEquals('desc2', $data->description
);
828 * Provider for various user preferences.
832 public function export_user_preference_provider() {
843 base64_encode('value'),
846 'long description' => [
850 'This is a much longer description which actually states what this is used for. Blah blah blah.',
856 * Test that exported data is human readable.
858 * @dataProvider unescaped_unicode_export_provider
859 * @param string $text
861 public function test_export_data_unescaped_unicode($text) {
862 $context = \context_system
::instance();
864 $data = (object) ['key' => $text];
866 $writer = $this->get_writer_instance()
867 ->set_context($context)
868 ->export_data($subcontext, $data);
870 $fileroot = $this->fetch_exported_content($writer);
872 $contextpath = $this->get_context_path($context, $subcontext, 'data.json');
874 $json = $fileroot->getChild($contextpath)->getContent();
875 $this->assertRegExp("/$text/", $json);
877 $expanded = json_decode($json);
878 $this->assertEquals($data, $expanded);
882 * Test that exported metadata is human readable.
884 * @dataProvider unescaped_unicode_export_provider
885 * @param string $text
887 public function test_export_metadata_unescaped_unicode($text) {
888 $context = \context_system
::instance();
889 $subcontext = ['a', 'b', 'c'];
891 $writer = $this->get_writer_instance()
892 ->set_context($context)
893 ->export_metadata($subcontext, $text, $text, $text);
895 $fileroot = $this->fetch_exported_content($writer);
897 $contextpath = $this->get_context_path($context, $subcontext, 'metadata.json');
899 $json = $fileroot->getChild($contextpath)->getContent();
900 $this->assertRegExp("/$text.*$text.*$text/", $json);
902 $expanded = json_decode($json);
903 $this->assertTrue(isset($expanded->$text));
904 $this->assertEquals($text, $expanded->$text->value
);
905 $this->assertEquals($text, $expanded->$text->description
);
909 * Test that exported related data is human readable.
911 * @dataProvider unescaped_unicode_export_provider
912 * @param string $text
914 public function test_export_related_data_unescaped_unicode($text) {
915 $context = \context_system
::instance();
917 $data = (object) ['key' => $text];
919 $writer = $this->get_writer_instance()
920 ->set_context($context)
921 ->export_related_data($subcontext, 'name', $data);
923 $fileroot = $this->fetch_exported_content($writer);
925 $contextpath = $this->get_context_path($context, $subcontext, 'name.json');
927 $json = $fileroot->getChild($contextpath)->getContent();
928 $this->assertRegExp("/$text/", $json);
930 $expanded = json_decode($json);
931 $this->assertEquals($data, $expanded);
935 * Test that exported user preference is human readable.
937 * @dataProvider unescaped_unicode_export_provider
938 * @param string $text
940 public function test_export_user_preference_unescaped_unicode($text) {
941 $context = \context_system
::instance();
942 $component = 'core_privacy';
944 $writer = $this->get_writer_instance()
945 ->set_context($context)
946 ->export_user_preference($component, $text, $text, $text);
948 $fileroot = $this->fetch_exported_content($writer);
950 $contextpath = $this->get_context_path($context, [get_string('userpreferences')], "{$component}.json");
952 $json = $fileroot->getChild($contextpath)->getContent();
953 $this->assertRegExp("/$text.*$text.*$text/", $json);
955 $expanded = json_decode($json);
956 $this->assertTrue(isset($expanded->$text));
957 $this->assertEquals($text, $expanded->$text->value
);
958 $this->assertEquals($text, $expanded->$text->description
);
962 * Provider for various user preferences.
966 public function unescaped_unicode_export_provider() {
968 'Unicode' => ['ةكءيٓپچژکگیٹڈڑہھےâîûğŞAaÇÖáǽ你好!'],
973 * Get a fresh content writer.
975 * @return moodle_content_writer
977 public function get_writer_instance() {
978 $factory = $this->createMock(writer
::class);
979 return new moodle_content_writer($factory);
983 * Fetch the exported content for inspection.
985 * @param moodle_content_writer $writer
986 * @return \org\bovigo\vfs\vfsStreamDirectory
988 protected function fetch_exported_content(moodle_content_writer
$writer) {
990 ->set_context(\context_system
::instance())
991 ->finalise_content();
993 $fileroot = \org\bovigo\vfs\vfsStream
::setup('root');
995 $target = \org\bovigo\vfs\vfsStream
::url('root');
996 $fp = get_file_packer();
997 $fp->extract_to_pathname($export, $target);
1003 * Determine the path for the current context.
1005 * Note: This is a wrapper around the real function.
1007 * @param \context $context The context being written
1008 * @param array $subcontext The subcontext path
1009 * @param string $name THe name of the file target
1010 * @return array The context path.
1012 protected function get_context_path($context, $subcontext = null, $name = '') {
1013 $rc = new ReflectionClass(moodle_content_writer
::class);
1014 $writer = $this->get_writer_instance();
1015 $writer->set_context($context);
1017 if (null === $subcontext) {
1018 $rcm = $rc->getMethod('get_context_path');
1019 $rcm->setAccessible(true);
1020 return $rcm->invoke($writer);
1022 $rcm = $rc->getMethod('get_path');
1023 $rcm->setAccessible(true);
1024 return $rcm->invoke($writer, $subcontext, $name);
1029 * Test correct rewriting of @@PLUGINFILE@@ in the exported contents.
1031 * @dataProvider rewrite_pluginfile_urls_provider
1032 * @param string $filearea The filearea within that component.
1033 * @param int $itemid Which item those files belong to.
1034 * @param string $input Raw text as stored in the database.
1035 * @param string $expectedoutput Expected output of URL rewriting.
1037 public function test_rewrite_pluginfile_urls($filearea, $itemid, $input, $expectedoutput) {
1039 $writer = $this->get_writer_instance();
1040 $writer->set_context(\context_system
::instance());
1042 $realoutput = $writer->rewrite_pluginfile_urls([], 'core_test', $filearea, $itemid, $input);
1044 $this->assertEquals($expectedoutput, $realoutput);
1048 * Provides testable sample data for {@link self::test_rewrite_pluginfile_urls()}.
1052 public function rewrite_pluginfile_urls_provider() {
1057 '<p><img src="@@PLUGINFILE@@/hello.gif" /></p>',
1058 '<p><img src="_files/intro/hello.gif" /></p>',
1060 'nonzeroitemid' => [
1061 'submission_content',
1063 '<p><img src="@@PLUGINFILE@@/first.png" alt="First" /></p>',
1064 '<p><img src="_files/submission_content/34/first.png" alt="First" /></p>',
1069 '<a href="@@PLUGINFILE@@/embedded/docs/muhehe.exe">Click here!</a>',
1070 '<a href="_files/post_content/9889/embedded/docs/muhehe.exe">Click here!</a>',