MDL-35603 - Backup - Course import selector notice
[moodle.git] / backup / util / xml / tests / writer_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * @package   core_backup
19  * @category  phpunit
20  * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
21  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
24 defined('MOODLE_INTERNAL') || die();
26 // Include all the needed stuff
27 global $CFG;
28 require_once($CFG->dirroot . '/backup/util/xml/xml_writer.class.php');
29 require_once($CFG->dirroot . '/backup/util/xml/output/xml_output.class.php');
30 require_once($CFG->dirroot . '/backup/util/xml/output/memory_xml_output.class.php');
31 require_once($CFG->dirroot . '/backup/util/xml/contenttransformer/xml_contenttransformer.class.php');
33 /**
34  * xml_writer tests
35  */
36 class xml_writer_testcase extends basic_testcase {
38     /**
39      * test xml_writer public methods
40      */
41     function test_xml_writer_public_api() {
42         global $CFG;
43         // Instantiate xml_output
44         $xo = new memory_xml_output();
45         $this->assertTrue($xo instanceof xml_output);
47         // Instantiate xml_writer with null xml_output
48         try {
49             $xw = new mock_xml_writer(null);
50             $this->assertTrue(false, 'xml_writer_exception expected');
51         } catch (exception $e) {
52             $this->assertTrue($e instanceof xml_writer_exception);
53             $this->assertEquals($e->errorcode, 'invalid_xml_output');
54         }
56         // Instantiate xml_writer with wrong xml_output object
57         try {
58             $xw = new mock_xml_writer(new stdclass());
59             $this->assertTrue(false, 'xml_writer_exception expected');
60         } catch (exception $e) {
61             $this->assertTrue($e instanceof xml_writer_exception);
62             $this->assertEquals($e->errorcode, 'invalid_xml_output');
63         }
65         // Instantiate xml_writer with wrong xml_contenttransformer object
66         try {
67             $xw = new mock_xml_writer($xo, new stdclass());
68             $this->assertTrue(false, 'xml_writer_exception expected');
69         } catch (exception $e) {
70             $this->assertTrue($e instanceof xml_writer_exception);
71             $this->assertEquals($e->errorcode, 'invalid_xml_contenttransformer');
72         }
74         // Instantiate xml_writer and start it twice
75         $xw = new mock_xml_writer($xo);
76         $xw->start();
77         try {
78             $xw->start();
79             $this->assertTrue(false, 'xml_writer_exception expected');
80         } catch (exception $e) {
81             $this->assertTrue($e instanceof xml_writer_exception);
82             $this->assertEquals($e->errorcode, 'xml_writer_already_started');
83         }
85         // Instantiate xml_writer and stop it twice
86         $xo = new memory_xml_output();
87         $xw = new mock_xml_writer($xo);
88         $xw->start();
89         $xw->stop();
90         try {
91             $xw->stop();
92             $this->assertTrue(false, 'xml_writer_exception expected');
93         } catch (exception $e) {
94             $this->assertTrue($e instanceof xml_writer_exception);
95             $this->assertEquals($e->errorcode, 'xml_writer_already_stopped');
96         }
98         // Stop writer without starting it
99         $xo = new memory_xml_output();
100         $xw = new mock_xml_writer($xo);
101         try {
102             $xw->stop();
103             $this->assertTrue(false, 'xml_writer_exception expected');
104         } catch (exception $e) {
105             $this->assertTrue($e instanceof xml_writer_exception);
106             $this->assertEquals($e->errorcode, 'xml_writer_not_started');
107         }
109         // Start writer after stopping it
110         $xo = new memory_xml_output();
111         $xw = new mock_xml_writer($xo);
112         $xw->start();
113         $xw->stop();
114         try {
115             $xw->start();
116             $this->assertTrue(false, 'xml_writer_exception expected');
117         } catch (exception $e) {
118             $this->assertTrue($e instanceof xml_writer_exception);
119             $this->assertEquals($e->errorcode, 'xml_writer_already_stopped');
120         }
122         // Try to set prologue/schema after start
123         $xo = new memory_xml_output();
124         $xw = new mock_xml_writer($xo);
125         $xw->start();
126         try {
127             $xw->set_nonamespace_schema('http://moodle.org');
128             $this->assertTrue(false, 'xml_writer_exception expected');
129         } catch (exception $e) {
130             $this->assertTrue($e instanceof xml_writer_exception);
131             $this->assertEquals($e->errorcode, 'xml_writer_already_started');
132         }
133         try {
134             $xw->set_prologue('sweet prologue');
135             $this->assertTrue(false, 'xml_writer_exception expected');
136         } catch (exception $e) {
137             $this->assertTrue($e instanceof xml_writer_exception);
138             $this->assertEquals($e->errorcode, 'xml_writer_already_started');
139         }
141         // Instantiate properly with memory_xml_output, start and stop.
142         // Must get default UTF-8 prologue
143         $xo = new memory_xml_output();
144         $xw = new mock_xml_writer($xo);
145         $xw->start();
146         $xw->stop();
147         $this->assertEquals($xo->get_allcontents(), $xw->get_default_prologue());
149         // Instantiate, set prologue and schema, put 1 full tag and get results
150         $xo = new memory_xml_output();
151         $xw = new mock_xml_writer($xo);
152         $xw->set_prologue('CLEARLY WRONG PROLOGUE');
153         $xw->set_nonamespace_schema('http://moodle.org/littleschema');
154         $xw->start();
155         $xw->full_tag('TEST', 'Hello World!', array('id' => 1));
156         $xw->stop();
157         $result = $xo->get_allcontents();
158         // Perform various checks
159         $this->assertEquals(strpos($result, 'WRONG'), 8);
160         $this->assertEquals(strpos($result, '<TEST id="1"'), 22);
161         $this->assertEquals(strpos($result, 'xmlns:xsi='), 39);
162         $this->assertEquals(strpos($result, 'http://moodle.org/littleschema'), 128);
163         $this->assertEquals(strpos($result, 'Hello World'), 160);
164         $this->assertFalse(strpos($result, $xw->get_default_prologue()));
166         // Try to close one tag in wrong order
167         $xo = new memory_xml_output();
168         $xw = new mock_xml_writer($xo);
169         $xw->start();
170         $xw->begin_tag('first');
171         $xw->begin_tag('second');
172         try {
173             $xw->end_tag('first');
174             $this->assertTrue(false, 'xml_writer_exception expected');
175         } catch (exception $e) {
176             $this->assertTrue($e instanceof xml_writer_exception);
177             $this->assertEquals($e->errorcode, 'xml_writer_end_tag_no_match');
178         }
180         // Try to close one tag before starting any tag
181         $xo = new memory_xml_output();
182         $xw = new mock_xml_writer($xo);
183         $xw->start();
184         try {
185             $xw->end_tag('first');
186             $this->assertTrue(false, 'xml_writer_exception expected');
187         } catch (exception $e) {
188             $this->assertTrue($e instanceof xml_writer_exception);
189             $this->assertEquals($e->errorcode, 'xml_writer_end_tag_no_match');
190         }
192         // Full tag without contents (null and empty string)
193         $xo = new memory_xml_output();
194         $xw = new mock_xml_writer($xo);
195         $xw->set_prologue(''); // empty prologue for easier matching
196         $xw->start();
197         $xw->full_tag('tagname', null, array('attrname' => 'attrvalue'));
198         $xw->full_tag('tagname2', '', array('attrname' => 'attrvalue'));
199         $xw->stop();
200         $result = $xo->get_allcontents();
201         $this->assertEquals($result, '<tagname attrname="attrvalue" /><tagname2 attrname="attrvalue"></tagname2>');
204         // Test case-folding is working
205         $xo = new memory_xml_output();
206         $xw = new mock_xml_writer($xo, null, true);
207         $xw->set_prologue(''); // empty prologue for easier matching
208         $xw->start();
209         $xw->full_tag('tagname', 'textcontent', array('attrname' => 'attrvalue'));
210         $xw->stop();
211         $result = $xo->get_allcontents();
212         $this->assertEquals($result, '<TAGNAME ATTRNAME="attrvalue">textcontent</TAGNAME>');
214         // Test UTF-8 chars in tag and attribute names, attr values and contents
215         $xo = new memory_xml_output();
216         $xw = new mock_xml_writer($xo);
217         $xw->set_prologue(''); // empty prologue for easier matching
218         $xw->start();
219         $xw->full_tag('áéíóú', 'ÁÉÍÓÚ', array('àèìòù' => 'ÀÈÌÒÙ'));
220         $xw->stop();
221         $result = $xo->get_allcontents();
222         $this->assertEquals($result, '<áéíóú àèìòù="ÀÈÌÒÙ">ÁÉÍÓÚ</áéíóú>');
224         // Try non-safe content in attributes
225         $xo = new memory_xml_output();
226         $xw = new mock_xml_writer($xo);
227         $xw->set_prologue(''); // empty prologue for easier matching
228         $xw->start();
229         $xw->full_tag('tagname', 'textcontent', array('attrname' => 'attr' . chr(27) . '\'"value'));
230         $xw->stop();
231         $result = $xo->get_allcontents();
232         $this->assertEquals($result, '<tagname attrname="attr\'&quot;value">textcontent</tagname>');
234         // Try non-safe content in text
235         $xo = new memory_xml_output();
236         $xw = new mock_xml_writer($xo);
237         $xw->set_prologue(''); // empty prologue for easier matching
238         $xw->start();
239         $xw->full_tag('tagname', "text\r\ncontent\rwith" . chr(27), array('attrname' => 'attrvalue'));
240         $xw->stop();
241         $result = $xo->get_allcontents();
242         $this->assertEquals($result, '<tagname attrname="attrvalue">text' . "\ncontent\n" . 'with</tagname>');
244         // Try to stop the writer without clossing all the open tags
245         $xo = new memory_xml_output();
246         $xw = new mock_xml_writer($xo);
247         $xw->start();
248         $xw->begin_tag('first');
249         try {
250             $xw->stop();
251             $this->assertTrue(false, 'xml_writer_exception expected');
252         } catch (exception $e) {
253             $this->assertTrue($e instanceof xml_writer_exception);
254             $this->assertEquals($e->errorcode, 'xml_writer_open_tags_remaining');
255         }
257         // Test simple transformer
258         $xo = new memory_xml_output();
259         $xt = new mock_xml_contenttransformer();
260         $xw = new mock_xml_writer($xo, $xt);
261         $xw->set_prologue(''); // empty prologue for easier matching
262         $xw->start();
263         $xw->full_tag('tagname', null, array('attrname' => 'attrvalue'));
264         $xw->full_tag('tagname2', 'somecontent', array('attrname' => 'attrvalue'));
265         $xw->stop();
266         $result = $xo->get_allcontents();
267         $this->assertEquals($result, '<tagname attrname="attrvalue" /><tagname2 attrname="attrvalue">testsomecontent</tagname2>');
269         // Build a complex XML file and test results against stored file in fixtures
270         $xo = new memory_xml_output();
271         $xw = new mock_xml_writer($xo);
272         $xw->start();
273         $xw->begin_tag('toptag', array('name' => 'toptag', 'level' => 1, 'path' => '/toptag'));
274         $xw->full_tag('secondtag', 'secondvalue', array('name' => 'secondtag', 'level' => 2, 'path' => '/toptag/secondtag', 'value' => 'secondvalue'));
275         $xw->begin_tag('thirdtag', array('name' => 'thirdtag', 'level' => 2, 'path' => '/toptag/thirdtag'));
276         $xw->full_tag('onevalue', 'onevalue', array('name' => 'onevalue', 'level' => 3, 'path' => '/toptag/thirdtag/onevalue'));
277         $xw->full_tag('onevalue', 'anothervalue', array('name' => 'onevalue', 'level' => 3, 'value' => 'anothervalue'));
278         $xw->full_tag('onevalue', 'yetanothervalue', array('name' => 'onevalue', 'level' => 3, 'value' => 'yetanothervalue'));
279         $xw->full_tag('twovalue', 'twovalue', array('name' => 'twovalue', 'level' => 3, 'path' => '/toptag/thirdtag/twovalue'));
280         $xw->begin_tag('forthtag', array('name' => 'forthtag', 'level' => 3, 'path' => '/toptag/thirdtag/forthtag'));
281         $xw->full_tag('innervalue', 'innervalue');
282         $xw->begin_tag('innertag');
283         $xw->begin_tag('superinnertag', array('name' => 'superinnertag', 'level' => 5));
284         $xw->full_tag('superinnervalue', 'superinnervalue', array('name' => 'superinnervalue', 'level' => 6));
285         $xw->end_tag('superinnertag');
286         $xw->end_tag('innertag');
287         $xw->end_tag('forthtag');
288         $xw->begin_tag('fifthtag', array('level' => 3));
289         $xw->begin_tag('sixthtag', array('level' => 4));
290         $xw->full_tag('seventh', 'seventh', array('level' => 5));
291         $xw->end_tag('sixthtag');
292         $xw->end_tag('fifthtag');
293         $xw->full_tag('finalvalue', 'finalvalue', array('name' => 'finalvalue', 'level' => 3, 'path' => '/toptag/thirdtag/finalvalue'));
294         $xw->full_tag('finalvalue');
295         $xw->end_tag('thirdtag');
296         $xw->end_tag('toptag');
297         $xw->stop();
298         $result = $xo->get_allcontents();
299         $fcontents = file_get_contents($CFG->dirroot . '/backup/util/xml/tests/fixtures/test1.xml');
301         // Normalise carriage return characters.
302         $fcontents = str_replace("\r\n", "\n", $fcontents);
303         $this->assertEquals(trim($result), trim($fcontents));
304     }
307 /*
308  * helper extended xml_writer class that makes some methods public for testing
309  */
310 class mock_xml_writer extends xml_writer {
311     public function get_default_prologue() {
312         return parent::get_default_prologue();
313     }
316 /*
317  * helper extended xml_contenttransformer prepending "test" to all the notnull contents
318  */
319 class mock_xml_contenttransformer extends xml_contenttransformer {
320     public function process($content) {
321         return is_null($content) ? null : 'test' . $content;
322     }