weekly release 2.1dev
[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');
4c7f6ac6 34require_once($CFG->dirroot . '/backup/util/xml/parser/processors/grouped_parser_processor.class.php');
be866f9d
EL
35
36/*
37 * progressive_parser and progressive_parser_processor tests
38 */
39class progressive_parser_test extends UnitTestCase {
40
41 public static $includecoverage = array('backup/util/xml/parser');
42 public static $excludecoverage = array('backup/util/xml/parser/simpletest');
43
44 /*
45 * test progressive_parser public methods
46 */
47 function test_parser_public_api() {
48 global $CFG;
49 // Instantiate progressive_parser
50 $pp = new progressive_parser();
51 $this->assertTrue($pp instanceof progressive_parser);
52 $pr = new mock_parser_processor();
53 $this->assertTrue($pr instanceof progressive_parser_processor);
54
55 // Try to process without processor
56 try {
57 $pp->process();
58 $this->assertTrue(false);
59 } catch (exception $e) {
60 $this->assertTrue($e instanceof progressive_parser_exception);
61 $this->assertEqual($e->errorcode, 'undefined_parser_processor');
62 }
63
64 // Assign processor to parser
65 $pp->set_processor($pr);
66
67 // Try to process without file and contents
68 try {
69 $pp->process();
70 $this->assertTrue(false);
71 } catch (exception $e) {
72 $this->assertTrue($e instanceof progressive_parser_exception);
73 $this->assertEqual($e->errorcode, 'undefined_xml_to_parse');
74 }
75
76 // Assign *invalid* processor to parser
77 try {
78 $pp->set_processor(new stdClass());
79 $this->assertTrue(false);
80 } catch (exception $e) {
81 $this->assertTrue($e instanceof progressive_parser_exception);
82 $this->assertEqual($e->errorcode, 'invalid_parser_processor');
83 }
84
85 // Set file from fixtures (test1.xml) and process it
86 $pp = new progressive_parser();
87 $pr = new mock_parser_processor();
88 $pp->set_processor($pr);
89 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test1.xml');
90 $pp->process();
91 $serfromfile = serialize($pr->get_chunks()); // Get serialized results (to compare later)
92 // Set *unexisting* file from fixtures
93 try {
94 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test0.xml');
95 $this->assertTrue(false);
96 } catch (exception $e) {
97 $this->assertTrue($e instanceof progressive_parser_exception);
98 $this->assertEqual($e->errorcode, 'invalid_file_to_parse');
99 }
100
101 // Set contents from fixtures (test1.xml) and process it
102 $pp = new progressive_parser();
103 $pr = new mock_parser_processor();
104 $pp->set_processor($pr);
105 $pp->set_contents(file_get_contents($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test1.xml'));
106 $pp->process();
107 $serfrommemory = serialize($pr->get_chunks()); // Get serialized results (to compare later)
108 // Set *empty* contents
109 try {
110 $pp->set_contents('');
111 $this->assertTrue(false);
112 } catch (exception $e) {
113 $this->assertTrue($e instanceof progressive_parser_exception);
114 $this->assertEqual($e->errorcode, 'invalid_contents_to_parse');
115 }
116
117 // Check that both results from file processing and content processing are equal
118 $this->assertEqual($serfromfile, $serfrommemory);
119
120 // Check case_folding is working ok
121 $pp = new progressive_parser(true);
122 $pr = new mock_parser_processor();
123 $pp->set_processor($pr);
124 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test1.xml');
125 $pp->process();
126 $chunks = $pr->get_chunks();
127 $this->assertTrue($chunks[0]['path'] === '/FIRSTTAG');
128 $this->assertTrue($chunks[0]['tags']['SECONDTAG']['name'] === 'SECONDTAG');
129 $this->assertTrue($chunks[0]['tags']['SECONDTAG']['attrs']['NAME'] === 'secondtag');
130
131 // Check invalid XML exception is working ok
132 $pp = new progressive_parser(true);
133 $pr = new mock_parser_processor();
134 $pp->set_processor($pr);
135 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test2.xml');
136 try {
137 $pp->process();
138 } catch (exception $e) {
139 $this->assertTrue($e instanceof progressive_parser_exception);
140 $this->assertEqual($e->errorcode, 'xml_parsing_error');
141 }
142
143 // Check double process throws exception
144 $pp = new progressive_parser(true);
145 $pr = new mock_parser_processor();
146 $pp->set_processor($pr);
147 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test1.xml');
148 $pp->process();
149 try { // Second process, will throw exception
150 $pp->process();
151 $this->assertTrue(false);
152 } catch (exception $e) {
153 $this->assertTrue($e instanceof progressive_parser_exception);
154 $this->assertEqual($e->errorcode, 'progressive_parser_already_used');
155 }
156 }
157
158 /*
159 * test progressive_parser parsing results using testing_parser_processor and test1.xml
160 * auto-described file from fixtures
161 */
162 function test_parser_results() {
163 global $CFG;
164 // Instantiate progressive_parser
165 $pp = new progressive_parser();
166 // Instantiate processor, passing the unit test as param
167 $pr = new mock_auto_parser_processor($this);
168 $this->assertTrue($pr instanceof progressive_parser_processor);
169 // Assign processor to parser
170 $pp->set_processor($pr);
171 // Set file from fixtures
172 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test3.xml');
173 // Process the file, the autotest processor will perform a bunch of automatic tests
174 $pp->process();
175 // Get processor debug info
176 $debug = $pr->debug_info();
177 $this->assertTrue(is_array($debug));
178 $this->assertTrue(array_key_exists('chunks', $debug));
179 // Check the number of chunks is correct for the file
180 $this->assertEqual($debug['chunks'], 10);
181 }
182
183 /*
184 * test progressive_parser parsing results using simplified_parser_processor and test4.xml
185 * (one simple glossary backup file example)
186 */
187 function test_simplified_parser_results() {
188 global $CFG;
189 // Instantiate progressive_parser
190 $pp = new progressive_parser();
191 // Instantiate simplified_parser_processor declaring the interesting paths
192 $pr = new mock_simplified_parser_processor(array(
193 '/activity',
194 '/activity/glossary',
195 '/activity/glossary/entries/entry',
196 '/activity/glossary/entries/entry/aliases/alias',
197 '/activity/glossary/entries/entry/ratings/rating',
4c7f6ac6
EL
198 '/activity/glossary/categories/category',
199 '/activity/glossary/onetest',
200 '/activity/glossary/othertest'));
be866f9d
EL
201 $this->assertTrue($pr instanceof progressive_parser_processor);
202 // Assign processor to parser
203 $pp->set_processor($pr);
204 // Set file from fixtures
205 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test4.xml');
206 // Process the file
207 $pp->process();
208 // Get processor debug info
209 $debug = $pr->debug_info();
210 $this->assertTrue(is_array($debug));
211 $this->assertTrue(array_key_exists('chunks', $debug));
212
213 // Check the number of chunks is correct for the file
4c7f6ac6 214 $this->assertEqual($debug['chunks'], 12);
be866f9d
EL
215 // Get all the simplified chunks and perform various validations
216 $chunks = $pr->get_chunks();
4c7f6ac6
EL
217 // Check we have received the correct number of chunks
218 $this->assertEqual(count($chunks), 12);
be866f9d
EL
219
220 // chunk[0] (/activity) tests
221 $this->assertEqual(count($chunks[0]), 3);
222 $this->assertEqual($chunks[0]['path'], '/activity');
223 $this->assertEqual($chunks[0]['level'],'2');
224 $tags = $chunks[0]['tags'];
225 $this->assertEqual(count($tags), 4);
226 $this->assertEqual($tags['id'], 1);
227 $this->assertEqual($tags['moduleid'], 5);
228 $this->assertEqual($tags['modulename'], 'glossary');
229 $this->assertEqual($tags['contextid'], 26);
230 $this->assertEqual($chunks[0]['level'],'2');
231
232 // chunk[1] (/activity/glossary) tests
233 $this->assertEqual(count($chunks[1]), 3);
234 $this->assertEqual($chunks[1]['path'], '/activity/glossary');
235 $this->assertEqual($chunks[1]['level'],'3');
236 $tags = $chunks[1]['tags'];
237 $this->assertEqual(count($tags), 24);
238 $this->assertEqual($tags['id'], 1);
239 $this->assertEqual($tags['intro'], '<p>One simple glossary to test backup &amp; restore. Here it\'s the standard image:</p>'.
240 "\n".
241 '<p><img src="@@PLUGINFILE@@/88_31.png" alt="pwd by moodle" width="88" height="31" /></p>');
242 $this->assertEqual($tags['timemodified'], 1275639747);
243 $this->assertTrue(!isset($tags['categories']));
244
245 // chunk[5] (second /activity/glossary/entries/entry) tests
246 $this->assertEqual(count($chunks[5]), 3);
247 $this->assertEqual($chunks[5]['path'], '/activity/glossary/entries/entry');
248 $this->assertEqual($chunks[5]['level'],'5');
249 $tags = $chunks[5]['tags'];
250 $this->assertEqual(count($tags), 15);
251 $this->assertEqual($tags['id'], 2);
252 $this->assertEqual($tags['concept'], 'cat');
253 $this->assertTrue(!isset($tags['aliases']));
254 $this->assertTrue(!isset($tags['entries']));
255
256 // chunk[6] (second /activity/glossary/entries/entry/aliases/alias) tests
257 $this->assertEqual(count($chunks[6]), 3);
258 $this->assertEqual($chunks[6]['path'], '/activity/glossary/entries/entry/aliases/alias');
259 $this->assertEqual($chunks[6]['level'],'7');
260 $tags = $chunks[6]['tags'];
261 $this->assertEqual(count($tags), 2);
262 $this->assertEqual($tags['id'], 2);
263 $this->assertEqual($tags['alias_text'], 'cats');
264
4c7f6ac6 265 // chunk[7] (second /activity/glossary/entries/entry/aliases/alias) tests
be866f9d 266 $this->assertEqual(count($chunks[7]), 3);
4c7f6ac6 267 $this->assertEqual($chunks[7]['path'], '/activity/glossary/entries/entry/aliases/alias');
be866f9d
EL
268 $this->assertEqual($chunks[7]['level'],'7');
269 $tags = $chunks[7]['tags'];
4c7f6ac6
EL
270 $this->assertEqual(count($tags), 2);
271 $this->assertEqual($tags['id'], 3);
272 $this->assertEqual($tags['alias_text'], 'felines');
273
274 // chunk[8] (second /activity/glossary/entries/entry/ratings/rating) tests
275 $this->assertEqual(count($chunks[8]), 3);
276 $this->assertEqual($chunks[8]['path'], '/activity/glossary/entries/entry/ratings/rating');
277 $this->assertEqual($chunks[8]['level'],'7');
278 $tags = $chunks[8]['tags'];
be866f9d
EL
279 $this->assertEqual(count($tags), 6);
280 $this->assertEqual($tags['id'], 1);
281 $this->assertEqual($tags['timemodified'], '1275639779');
4c7f6ac6
EL
282
283 // chunk[9] (first /activity/glossary/onetest) tests
284 $this->assertEqual(count($chunks[9]), 3);
285 $this->assertEqual($chunks[9]['path'], '/activity/glossary/onetest');
286 $this->assertEqual($chunks[9]['level'],'4');
287 $tags = $chunks[9]['tags'];
288 $this->assertEqual(count($tags), 2);
289 $this->assertEqual($tags['name'], 1);
290 $this->assertEqual($tags['value'], 1);
291
292 // chunk[10] (second /activity/glossary/onetest) tests
293 $this->assertEqual(count($chunks[10]), 3);
294 $this->assertEqual($chunks[10]['path'], '/activity/glossary/onetest');
295 $this->assertEqual($chunks[10]['level'],'4');
296 $tags = $chunks[10]['tags'];
297 $this->assertEqual(count($tags), 2);
298 $this->assertEqual($tags['name'], 2);
299 $this->assertEqual($tags['value'], 2);
300
301 // chunk[11] (first /activity/glossary/othertest) tests
302 // note we don't allow repeated "final" element, so we only return the last one
303 $this->assertEqual(count($chunks[11]), 3);
304 $this->assertEqual($chunks[11]['path'], '/activity/glossary/othertest');
305 $this->assertEqual($chunks[11]['level'],'4');
306 $tags = $chunks[11]['tags'];
307 $this->assertEqual(count($tags), 2);
308 $this->assertEqual($tags['name'], 4);
309 $this->assertEqual($tags['value'], 5);
310 }
311
312 /*
313 * test progressive_parser parsing results using grouped_parser_processor and test4.xml
314 * (one simple glossary backup file example)
315 */
316 function test_grouped_parser_results() {
317 global $CFG;
318 // Instantiate progressive_parser
319 $pp = new progressive_parser();
320 // Instantiate grouped_parser_processor
321 $pr = new mock_grouped_parser_processor();
322 // Add interesting paths
323 $pr->add_path('/activity');
324 $pr->add_path('/activity/glossary', true);
325 $pr->add_path('/activity/glossary/entries/entry');
326 $pr->add_path('/activity/glossary/entries/entry/aliases/alias');
327 $pr->add_path('/activity/glossary/entries/entry/ratings/rating');
328 $pr->add_path('/activity/glossary/categories/category');
329 $pr->add_path('/activity/glossary/onetest');
330 $pr->add_path('/activity/glossary/othertest');
331 $this->assertTrue($pr instanceof progressive_parser_processor);
332 // Assign processor to parser
333 $pp->set_processor($pr);
334 // Set file from fixtures
335 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test4.xml');
336 // Process the file
337 $pp->process();
338 // Get processor debug info
339 $debug = $pr->debug_info();
340 $this->assertTrue(is_array($debug));
341 $this->assertTrue(array_key_exists('chunks', $debug));
342
343 // Check the number of chunks is correct for the file
344 $this->assertEqual($debug['chunks'], 2);
345 // Get all the simplified chunks and perform various validations
346 $chunks = $pr->get_chunks();
347 // Check we have received the correct number of chunks
348 $this->assertEqual(count($chunks), 2);
349
350 // chunk[0] (/activity) tests
351 $this->assertEqual(count($chunks[0]), 3);
352 $this->assertEqual($chunks[0]['path'], '/activity');
353 $this->assertEqual($chunks[0]['level'],'2');
354 $tags = $chunks[0]['tags'];
355 $this->assertEqual(count($tags), 4);
356 $this->assertEqual($tags['id'], 1);
357 $this->assertEqual($tags['moduleid'], 5);
358 $this->assertEqual($tags['modulename'], 'glossary');
359 $this->assertEqual($tags['contextid'], 26);
360 $this->assertEqual($chunks[0]['level'],'2');
361
362 // chunk[1] (grouped /activity/glossary tests)
363 $this->assertEqual(count($chunks[1]), 3);
364 $this->assertEqual($chunks[1]['path'], '/activity/glossary');
365 $this->assertEqual($chunks[1]['level'],'3');
366 $tags = $chunks[1]['tags'];
367 $this->assertEqual(count($tags), 27);
368 $this->assertEqual($tags['id'], 1);
369 $this->assertEqual($tags['intro'], '<p>One simple glossary to test backup &amp; restore. Here it\'s the standard image:</p>'.
370 "\n".
371 '<p><img src="@@PLUGINFILE@@/88_31.png" alt="pwd by moodle" width="88" height="31" /></p>');
372 $this->assertEqual($tags['timemodified'], 1275639747);
373 $this->assertTrue(!isset($tags['categories']));
374 $this->assertTrue(isset($tags['entries']));
375 $this->assertTrue(isset($tags['onetest']));
376 $this->assertTrue(isset($tags['othertest']));
377
378 // Various tests under the entries
379 $entries = $chunks[1]['tags']['entries']['entry'];
380 $this->assertEqual(count($entries), 2);
381
382 // First entry
383 $entry1 = $entries[0];
384 $this->assertEqual(count($entry1), 17);
385 $this->assertEqual($entry1['id'], 1);
386 $this->assertEqual($entry1['userid'], 2);
387 $this->assertEqual($entry1['concept'], 'dog');
388 $this->assertEqual($entry1['definition'], '<p>Traditional enemies of cats</p>');
389 $this->assertTrue(isset($entry1['aliases']));
390 $this->assertTrue(isset($entry1['ratings']));
391 // aliases of first entry
392 $aliases = $entry1['aliases']['alias'];
393 $this->assertEqual(count($aliases), 1);
394 // first alias
395 $alias1 = $aliases[0];
396 $this->assertEqual(count($alias1), 2);
397 $this->assertEqual($alias1['id'], 1);
398 $this->assertEqual($alias1['alias_text'], 'dogs');
399 // ratings of first entry
400 $ratings = $entry1['ratings']['rating'];
401 $this->assertEqual(count($ratings), 1);
402 // first rating
403 $rating1 = $ratings[0];
404 $this->assertEqual(count($rating1), 6);
405 $this->assertEqual($rating1['id'], 2);
406 $this->assertEqual($rating1['value'], 6);
407 $this->assertEqual($rating1['timemodified'], '1275639797');
408
409 // Second entry
410 $entry2 = $entries[1];
411 $this->assertEqual(count($entry2), 17);
412 $this->assertEqual($entry2['id'], 2);
413 $this->assertEqual($entry2['userid'], 2);
414 $this->assertEqual($entry2['concept'], 'cat');
415 $this->assertEqual($entry2['definition'], '<p>traditional enemies of dogs</p>');
416 $this->assertTrue(isset($entry2['aliases']));
417 $this->assertTrue(isset($entry2['ratings']));
418 // aliases of first entry
419 $aliases = $entry2['aliases']['alias'];
420 $this->assertEqual(count($aliases), 2);
421 // first alias
422 $alias1 = $aliases[0];
423 $this->assertEqual(count($alias1), 2);
424 $this->assertEqual($alias1['id'], 2);
425 $this->assertEqual($alias1['alias_text'], 'cats');
426 // second alias
427 $alias2 = $aliases[1];
428 $this->assertEqual(count($alias2), 2);
429 $this->assertEqual($alias2['id'], 3);
430 $this->assertEqual($alias2['alias_text'], 'felines');
431 // ratings of first entry
432 $ratings = $entry2['ratings']['rating'];
433 $this->assertEqual(count($ratings), 1);
434 // first rating
435 $rating1 = $ratings[0];
436 $this->assertEqual(count($rating1), 6);
437 $this->assertEqual($rating1['id'], 1);
438 $this->assertEqual($rating1['value'], 5);
439 $this->assertEqual($rating1['scaleid'], 10);
440
441 // Onetest test (only 1 level nested)
442 $onetest = $tags['onetest'];
443 $this->assertEqual(count($onetest), 2);
444 $this->assertEqual(count($onetest[0]), 2);
445 $this->assertEqual($onetest[0]['name'], 1);
446 $this->assertEqual($onetest[0]['value'], 1);
447 $this->assertEqual(count($onetest[1]), 2);
448 $this->assertEqual($onetest[1]['name'], 2);
449 $this->assertEqual($onetest[1]['value'], 2);
450
451 // Other test (0 level nested, only last one is retrieved)
452 $othertest = $tags['othertest'];
453 $this->assertEqual(count($othertest), 1);
454 $this->assertEqual(count($othertest[0]), 2);
455 $this->assertEqual($othertest[0]['name'], 4);
456 $this->assertEqual($othertest[0]['value'], 5);
be866f9d
EL
457 }
458}
459
460/*
461 * helper processor able to perform various auto-cheks based on attributes while processing
462 * the test1.xml file available in the fixtures dir. It performs these checks:
463 * - name equal to "name" attribute of the tag (if present)
464 * - level equal to "level" attribute of the tag (if present)
465 * - path + tagname equal to "path" attribute of the tag (if present)
466 * - cdata, if not empty is:
467 * - equal to "value" attribute of the tag (if present)
468 * - else, equal to tag name
469 *
470 * We pass the whole UnitTestCase object to the processor in order to be
471 * able to perform the tests in the straight in the process
472 */
473class mock_auto_parser_processor extends progressive_parser_processor {
474
475 private $utc = null; // To store the unit test case
476
477 public function __construct($unit_test_case) {
478 parent::__construct();
479 $this->utc = $unit_test_case;
480 }
481
482 public function process_chunk($data) {
483 // Perform auto-checks based in the rules above
484 if (isset($data['tags'])) {
485 foreach ($data['tags'] as $tag) {
486 if (isset($tag['attrs']['name'])) { // name tests
487 $this->utc->assertEqual($tag['name'], $tag['attrs']['name']);
488 }
489 if (isset($tag['attrs']['level'])) { // level tests
490 $this->utc->assertEqual($data['level'], $tag['attrs']['level']);
491 }
492 if (isset($tag['attrs']['path'])) { // path tests
493 $this->utc->assertEqual(rtrim($data['path'], '/') . '/' . $tag['name'], $tag['attrs']['path']);
494 }
495 if (!empty($tag['cdata'])) { // cdata tests
496 if (isset($tag['attrs']['value'])) {
497 $this->utc->assertEqual($tag['cdata'], $tag['attrs']['value']);
498 } else {
499 $this->utc->assertEqual($tag['cdata'], $tag['name']);
500 }
501 }
502 }
503 }
504 }
505}
506
507/*
508 * helper processor that accumulates all the chunks, resturning them with the get_chunks() method
509 */
510class mock_parser_processor extends progressive_parser_processor {
511
512 private $chunksarr = array(); // To accumulate the found chunks
513
514 public function process_chunk($data) {
515 $this->chunksarr[] = $data;
516 }
517
518 public function get_chunks() {
519 return $this->chunksarr;
520 }
521}
522
523/*
524 * helper processor that accumulates simplified chunks, returning them with the get_chunks() method
525 */
526class mock_simplified_parser_processor extends simplified_parser_processor {
527
528 private $chunksarr = array(); // To accumulate the found chunks
529
530 public function dispatch_chunk($data) {
531 $this->chunksarr[] = $data;
532 }
533
534 public function get_chunks() {
535 return $this->chunksarr;
536 }
537}
4c7f6ac6
EL
538
539/*
540 * helper processor that accumulates grouped chunks, returning them with the get_chunks() method
541 */
542class mock_grouped_parser_processor extends grouped_parser_processor {
543
544 private $chunksarr = array(); // To accumulate the found chunks
545
546 public function dispatch_chunk($data) {
547 $this->chunksarr[] = $data;
548 }
549
550 public function get_chunks() {
551 return $this->chunksarr;
552 }
553}