MDL-22699 xml parser - to be used by restore
[moodle.git] / backup / util / xml / parser / simpletest / testparser.php
CommitLineData
be866f9d
EL
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * @package moodlecore
20 * @subpackage backup-tests
21 * @copyright 2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25// Prevent direct access to this file
26if (!defined('MOODLE_INTERNAL')) {
27 die('Direct access to this script is forbidden.');
28}
29
30// Include all the needed stuff
31require_once($CFG->dirroot . '/backup/util/xml/parser/progressive_parser.class.php');
32require_once($CFG->dirroot . '/backup/util/xml/parser/processors/progressive_parser_processor.class.php');
33require_once($CFG->dirroot . '/backup/util/xml/parser/processors/simplified_parser_processor.class.php');
34
35/*
36 * progressive_parser and progressive_parser_processor tests
37 */
38class progressive_parser_test extends UnitTestCase {
39
40 public static $includecoverage = array('backup/util/xml/parser');
41 public static $excludecoverage = array('backup/util/xml/parser/simpletest');
42
43 /*
44 * test progressive_parser public methods
45 */
46 function test_parser_public_api() {
47 global $CFG;
48 // Instantiate progressive_parser
49 $pp = new progressive_parser();
50 $this->assertTrue($pp instanceof progressive_parser);
51 $pr = new mock_parser_processor();
52 $this->assertTrue($pr instanceof progressive_parser_processor);
53
54 // Try to process without processor
55 try {
56 $pp->process();
57 $this->assertTrue(false);
58 } catch (exception $e) {
59 $this->assertTrue($e instanceof progressive_parser_exception);
60 $this->assertEqual($e->errorcode, 'undefined_parser_processor');
61 }
62
63 // Assign processor to parser
64 $pp->set_processor($pr);
65
66 // Try to process without file and contents
67 try {
68 $pp->process();
69 $this->assertTrue(false);
70 } catch (exception $e) {
71 $this->assertTrue($e instanceof progressive_parser_exception);
72 $this->assertEqual($e->errorcode, 'undefined_xml_to_parse');
73 }
74
75 // Assign *invalid* processor to parser
76 try {
77 $pp->set_processor(new stdClass());
78 $this->assertTrue(false);
79 } catch (exception $e) {
80 $this->assertTrue($e instanceof progressive_parser_exception);
81 $this->assertEqual($e->errorcode, 'invalid_parser_processor');
82 }
83
84 // Set file from fixtures (test1.xml) and process it
85 $pp = new progressive_parser();
86 $pr = new mock_parser_processor();
87 $pp->set_processor($pr);
88 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test1.xml');
89 $pp->process();
90 $serfromfile = serialize($pr->get_chunks()); // Get serialized results (to compare later)
91 // Set *unexisting* file from fixtures
92 try {
93 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test0.xml');
94 $this->assertTrue(false);
95 } catch (exception $e) {
96 $this->assertTrue($e instanceof progressive_parser_exception);
97 $this->assertEqual($e->errorcode, 'invalid_file_to_parse');
98 }
99
100 // Set contents from fixtures (test1.xml) and process it
101 $pp = new progressive_parser();
102 $pr = new mock_parser_processor();
103 $pp->set_processor($pr);
104 $pp->set_contents(file_get_contents($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test1.xml'));
105 $pp->process();
106 $serfrommemory = serialize($pr->get_chunks()); // Get serialized results (to compare later)
107 // Set *empty* contents
108 try {
109 $pp->set_contents('');
110 $this->assertTrue(false);
111 } catch (exception $e) {
112 $this->assertTrue($e instanceof progressive_parser_exception);
113 $this->assertEqual($e->errorcode, 'invalid_contents_to_parse');
114 }
115
116 // Check that both results from file processing and content processing are equal
117 $this->assertEqual($serfromfile, $serfrommemory);
118
119 // Check case_folding is working ok
120 $pp = new progressive_parser(true);
121 $pr = new mock_parser_processor();
122 $pp->set_processor($pr);
123 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test1.xml');
124 $pp->process();
125 $chunks = $pr->get_chunks();
126 $this->assertTrue($chunks[0]['path'] === '/FIRSTTAG');
127 $this->assertTrue($chunks[0]['tags']['SECONDTAG']['name'] === 'SECONDTAG');
128 $this->assertTrue($chunks[0]['tags']['SECONDTAG']['attrs']['NAME'] === 'secondtag');
129
130 // Check invalid XML exception is working ok
131 $pp = new progressive_parser(true);
132 $pr = new mock_parser_processor();
133 $pp->set_processor($pr);
134 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test2.xml');
135 try {
136 $pp->process();
137 } catch (exception $e) {
138 $this->assertTrue($e instanceof progressive_parser_exception);
139 $this->assertEqual($e->errorcode, 'xml_parsing_error');
140 }
141
142 // Check double process throws exception
143 $pp = new progressive_parser(true);
144 $pr = new mock_parser_processor();
145 $pp->set_processor($pr);
146 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test1.xml');
147 $pp->process();
148 try { // Second process, will throw exception
149 $pp->process();
150 $this->assertTrue(false);
151 } catch (exception $e) {
152 $this->assertTrue($e instanceof progressive_parser_exception);
153 $this->assertEqual($e->errorcode, 'progressive_parser_already_used');
154 }
155 }
156
157 /*
158 * test progressive_parser parsing results using testing_parser_processor and test1.xml
159 * auto-described file from fixtures
160 */
161 function test_parser_results() {
162 global $CFG;
163 // Instantiate progressive_parser
164 $pp = new progressive_parser();
165 // Instantiate processor, passing the unit test as param
166 $pr = new mock_auto_parser_processor($this);
167 $this->assertTrue($pr instanceof progressive_parser_processor);
168 // Assign processor to parser
169 $pp->set_processor($pr);
170 // Set file from fixtures
171 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test3.xml');
172 // Process the file, the autotest processor will perform a bunch of automatic tests
173 $pp->process();
174 // Get processor debug info
175 $debug = $pr->debug_info();
176 $this->assertTrue(is_array($debug));
177 $this->assertTrue(array_key_exists('chunks', $debug));
178 // Check the number of chunks is correct for the file
179 $this->assertEqual($debug['chunks'], 10);
180 }
181
182 /*
183 * test progressive_parser parsing results using simplified_parser_processor and test4.xml
184 * (one simple glossary backup file example)
185 */
186 function test_simplified_parser_results() {
187 global $CFG;
188 // Instantiate progressive_parser
189 $pp = new progressive_parser();
190 // Instantiate simplified_parser_processor declaring the interesting paths
191 $pr = new mock_simplified_parser_processor(array(
192 '/activity',
193 '/activity/glossary',
194 '/activity/glossary/entries/entry',
195 '/activity/glossary/entries/entry/aliases/alias',
196 '/activity/glossary/entries/entry/ratings/rating',
197 '/activity/glossary/categories/category'));
198 $this->assertTrue($pr instanceof progressive_parser_processor);
199 // Assign processor to parser
200 $pp->set_processor($pr);
201 // Set file from fixtures
202 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test4.xml');
203 // Process the file
204 $pp->process();
205 // Get processor debug info
206 $debug = $pr->debug_info();
207 $this->assertTrue(is_array($debug));
208 $this->assertTrue(array_key_exists('chunks', $debug));
209
210 // Check the number of chunks is correct for the file
211 $this->assertEqual($debug['chunks'], 8);
212 // Get all the simplified chunks and perform various validations
213 $chunks = $pr->get_chunks();
214
215 // chunk[0] (/activity) tests
216 $this->assertEqual(count($chunks[0]), 3);
217 $this->assertEqual($chunks[0]['path'], '/activity');
218 $this->assertEqual($chunks[0]['level'],'2');
219 $tags = $chunks[0]['tags'];
220 $this->assertEqual(count($tags), 4);
221 $this->assertEqual($tags['id'], 1);
222 $this->assertEqual($tags['moduleid'], 5);
223 $this->assertEqual($tags['modulename'], 'glossary');
224 $this->assertEqual($tags['contextid'], 26);
225 $this->assertEqual($chunks[0]['level'],'2');
226
227 // chunk[1] (/activity/glossary) tests
228 $this->assertEqual(count($chunks[1]), 3);
229 $this->assertEqual($chunks[1]['path'], '/activity/glossary');
230 $this->assertEqual($chunks[1]['level'],'3');
231 $tags = $chunks[1]['tags'];
232 $this->assertEqual(count($tags), 24);
233 $this->assertEqual($tags['id'], 1);
234 $this->assertEqual($tags['intro'], '<p>One simple glossary to test backup &amp; restore. Here it\'s the standard image:</p>'.
235 "\n".
236 '<p><img src="@@PLUGINFILE@@/88_31.png" alt="pwd by moodle" width="88" height="31" /></p>');
237 $this->assertEqual($tags['timemodified'], 1275639747);
238 $this->assertTrue(!isset($tags['categories']));
239
240 // chunk[5] (second /activity/glossary/entries/entry) tests
241 $this->assertEqual(count($chunks[5]), 3);
242 $this->assertEqual($chunks[5]['path'], '/activity/glossary/entries/entry');
243 $this->assertEqual($chunks[5]['level'],'5');
244 $tags = $chunks[5]['tags'];
245 $this->assertEqual(count($tags), 15);
246 $this->assertEqual($tags['id'], 2);
247 $this->assertEqual($tags['concept'], 'cat');
248 $this->assertTrue(!isset($tags['aliases']));
249 $this->assertTrue(!isset($tags['entries']));
250
251 // chunk[6] (second /activity/glossary/entries/entry/aliases/alias) tests
252 $this->assertEqual(count($chunks[6]), 3);
253 $this->assertEqual($chunks[6]['path'], '/activity/glossary/entries/entry/aliases/alias');
254 $this->assertEqual($chunks[6]['level'],'7');
255 $tags = $chunks[6]['tags'];
256 $this->assertEqual(count($tags), 2);
257 $this->assertEqual($tags['id'], 2);
258 $this->assertEqual($tags['alias_text'], 'cats');
259
260 // chunk[7] (second /activity/glossary/entries/entry/ratings/rating) tests
261 $this->assertEqual(count($chunks[7]), 3);
262 $this->assertEqual($chunks[7]['path'], '/activity/glossary/entries/entry/ratings/rating');
263 $this->assertEqual($chunks[7]['level'],'7');
264 $tags = $chunks[7]['tags'];
265 $this->assertEqual(count($tags), 6);
266 $this->assertEqual($tags['id'], 1);
267 $this->assertEqual($tags['timemodified'], '1275639779');
268 }
269}
270
271/*
272 * helper processor able to perform various auto-cheks based on attributes while processing
273 * the test1.xml file available in the fixtures dir. It performs these checks:
274 * - name equal to "name" attribute of the tag (if present)
275 * - level equal to "level" attribute of the tag (if present)
276 * - path + tagname equal to "path" attribute of the tag (if present)
277 * - cdata, if not empty is:
278 * - equal to "value" attribute of the tag (if present)
279 * - else, equal to tag name
280 *
281 * We pass the whole UnitTestCase object to the processor in order to be
282 * able to perform the tests in the straight in the process
283 */
284class mock_auto_parser_processor extends progressive_parser_processor {
285
286 private $utc = null; // To store the unit test case
287
288 public function __construct($unit_test_case) {
289 parent::__construct();
290 $this->utc = $unit_test_case;
291 }
292
293 public function process_chunk($data) {
294 // Perform auto-checks based in the rules above
295 if (isset($data['tags'])) {
296 foreach ($data['tags'] as $tag) {
297 if (isset($tag['attrs']['name'])) { // name tests
298 $this->utc->assertEqual($tag['name'], $tag['attrs']['name']);
299 }
300 if (isset($tag['attrs']['level'])) { // level tests
301 $this->utc->assertEqual($data['level'], $tag['attrs']['level']);
302 }
303 if (isset($tag['attrs']['path'])) { // path tests
304 $this->utc->assertEqual(rtrim($data['path'], '/') . '/' . $tag['name'], $tag['attrs']['path']);
305 }
306 if (!empty($tag['cdata'])) { // cdata tests
307 if (isset($tag['attrs']['value'])) {
308 $this->utc->assertEqual($tag['cdata'], $tag['attrs']['value']);
309 } else {
310 $this->utc->assertEqual($tag['cdata'], $tag['name']);
311 }
312 }
313 }
314 }
315 }
316}
317
318/*
319 * helper processor that accumulates all the chunks, resturning them with the get_chunks() method
320 */
321class mock_parser_processor extends progressive_parser_processor {
322
323 private $chunksarr = array(); // To accumulate the found chunks
324
325 public function process_chunk($data) {
326 $this->chunksarr[] = $data;
327 }
328
329 public function get_chunks() {
330 return $this->chunksarr;
331 }
332}
333
334/*
335 * helper processor that accumulates simplified chunks, returning them with the get_chunks() method
336 */
337class mock_simplified_parser_processor extends simplified_parser_processor {
338
339 private $chunksarr = array(); // To accumulate the found chunks
340
341 public function dispatch_chunk($data) {
342 $this->chunksarr[] = $data;
343 }
344
345 public function get_chunks() {
346 return $this->chunksarr;
347 }
348}