MDL-37893 Take rid of old comment.
[moodle.git] / backup / util / xml / parser / tests / parser_test.php
CommitLineData
be866f9d 1<?php
be866f9d
EL
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/**
bb7898c6
PS
18 * @package core_backup
19 * @category phpunit
be866f9d
EL
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 */
23
bb7898c6 24defined('MOODLE_INTERNAL') || die();
be866f9d
EL
25
26// Include all the needed stuff
bb7898c6 27global $CFG;
be866f9d
EL
28require_once($CFG->dirroot . '/backup/util/xml/parser/progressive_parser.class.php');
29require_once($CFG->dirroot . '/backup/util/xml/parser/processors/progressive_parser_processor.class.php');
30require_once($CFG->dirroot . '/backup/util/xml/parser/processors/simplified_parser_processor.class.php');
4c7f6ac6 31require_once($CFG->dirroot . '/backup/util/xml/parser/processors/grouped_parser_processor.class.php');
be866f9d
EL
32
33/*
34 * progressive_parser and progressive_parser_processor tests
35 */
bb7898c6 36class progressive_parser_test extends advanced_testcase {
be866f9d
EL
37
38 /*
39 * test progressive_parser public methods
40 */
41 function test_parser_public_api() {
42 global $CFG;
43 // Instantiate progressive_parser
44 $pp = new progressive_parser();
45 $this->assertTrue($pp instanceof progressive_parser);
46 $pr = new mock_parser_processor();
47 $this->assertTrue($pr instanceof progressive_parser_processor);
48
49 // Try to process without processor
50 try {
51 $pp->process();
52 $this->assertTrue(false);
53 } catch (exception $e) {
54 $this->assertTrue($e instanceof progressive_parser_exception);
bb7898c6 55 $this->assertEquals($e->errorcode, 'undefined_parser_processor');
be866f9d
EL
56 }
57
58 // Assign processor to parser
59 $pp->set_processor($pr);
60
61 // Try to process without file and contents
62 try {
63 $pp->process();
64 $this->assertTrue(false);
65 } catch (exception $e) {
66 $this->assertTrue($e instanceof progressive_parser_exception);
bb7898c6 67 $this->assertEquals($e->errorcode, 'undefined_xml_to_parse');
be866f9d
EL
68 }
69
70 // Assign *invalid* processor to parser
71 try {
72 $pp->set_processor(new stdClass());
73 $this->assertTrue(false);
74 } catch (exception $e) {
75 $this->assertTrue($e instanceof progressive_parser_exception);
bb7898c6 76 $this->assertEquals($e->errorcode, 'invalid_parser_processor');
be866f9d
EL
77 }
78
79 // Set file from fixtures (test1.xml) and process it
80 $pp = new progressive_parser();
81 $pr = new mock_parser_processor();
82 $pp->set_processor($pr);
bb7898c6 83 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml');
be866f9d
EL
84 $pp->process();
85 $serfromfile = serialize($pr->get_chunks()); // Get serialized results (to compare later)
86 // Set *unexisting* file from fixtures
87 try {
bb7898c6 88 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test0.xml');
be866f9d
EL
89 $this->assertTrue(false);
90 } catch (exception $e) {
91 $this->assertTrue($e instanceof progressive_parser_exception);
bb7898c6 92 $this->assertEquals($e->errorcode, 'invalid_file_to_parse');
be866f9d
EL
93 }
94
95 // Set contents from fixtures (test1.xml) and process it
96 $pp = new progressive_parser();
97 $pr = new mock_parser_processor();
98 $pp->set_processor($pr);
bb7898c6 99 $pp->set_contents(file_get_contents($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml'));
be866f9d
EL
100 $pp->process();
101 $serfrommemory = serialize($pr->get_chunks()); // Get serialized results (to compare later)
102 // Set *empty* contents
103 try {
104 $pp->set_contents('');
105 $this->assertTrue(false);
106 } catch (exception $e) {
107 $this->assertTrue($e instanceof progressive_parser_exception);
bb7898c6 108 $this->assertEquals($e->errorcode, 'invalid_contents_to_parse');
be866f9d
EL
109 }
110
111 // Check that both results from file processing and content processing are equal
bb7898c6 112 $this->assertEquals($serfromfile, $serfrommemory);
be866f9d
EL
113
114 // Check case_folding is working ok
115 $pp = new progressive_parser(true);
116 $pr = new mock_parser_processor();
117 $pp->set_processor($pr);
bb7898c6 118 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml');
be866f9d
EL
119 $pp->process();
120 $chunks = $pr->get_chunks();
121 $this->assertTrue($chunks[0]['path'] === '/FIRSTTAG');
122 $this->assertTrue($chunks[0]['tags']['SECONDTAG']['name'] === 'SECONDTAG');
123 $this->assertTrue($chunks[0]['tags']['SECONDTAG']['attrs']['NAME'] === 'secondtag');
124
125 // Check invalid XML exception is working ok
126 $pp = new progressive_parser(true);
127 $pr = new mock_parser_processor();
128 $pp->set_processor($pr);
bb7898c6 129 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test2.xml');
be866f9d
EL
130 try {
131 $pp->process();
132 } catch (exception $e) {
133 $this->assertTrue($e instanceof progressive_parser_exception);
bb7898c6 134 $this->assertEquals($e->errorcode, 'xml_parsing_error');
be866f9d
EL
135 }
136
137 // Check double process throws exception
138 $pp = new progressive_parser(true);
139 $pr = new mock_parser_processor();
140 $pp->set_processor($pr);
bb7898c6 141 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test1.xml');
be866f9d
EL
142 $pp->process();
143 try { // Second process, will throw exception
144 $pp->process();
145 $this->assertTrue(false);
146 } catch (exception $e) {
147 $this->assertTrue($e instanceof progressive_parser_exception);
bb7898c6 148 $this->assertEquals($e->errorcode, 'progressive_parser_already_used');
be866f9d
EL
149 }
150 }
151
152 /*
153 * test progressive_parser parsing results using testing_parser_processor and test1.xml
154 * auto-described file from fixtures
155 */
156 function test_parser_results() {
157 global $CFG;
158 // Instantiate progressive_parser
159 $pp = new progressive_parser();
160 // Instantiate processor, passing the unit test as param
161 $pr = new mock_auto_parser_processor($this);
162 $this->assertTrue($pr instanceof progressive_parser_processor);
163 // Assign processor to parser
164 $pp->set_processor($pr);
165 // Set file from fixtures
bb7898c6 166 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test3.xml');
be866f9d
EL
167 // Process the file, the autotest processor will perform a bunch of automatic tests
168 $pp->process();
169 // Get processor debug info
170 $debug = $pr->debug_info();
171 $this->assertTrue(is_array($debug));
172 $this->assertTrue(array_key_exists('chunks', $debug));
173 // Check the number of chunks is correct for the file
bb7898c6 174 $this->assertEquals($debug['chunks'], 10);
be866f9d
EL
175 }
176
177 /*
178 * test progressive_parser parsing results using simplified_parser_processor and test4.xml
179 * (one simple glossary backup file example)
180 */
181 function test_simplified_parser_results() {
182 global $CFG;
183 // Instantiate progressive_parser
184 $pp = new progressive_parser();
185 // Instantiate simplified_parser_processor declaring the interesting paths
186 $pr = new mock_simplified_parser_processor(array(
187 '/activity',
188 '/activity/glossary',
189 '/activity/glossary/entries/entry',
190 '/activity/glossary/entries/entry/aliases/alias',
191 '/activity/glossary/entries/entry/ratings/rating',
4c7f6ac6
EL
192 '/activity/glossary/categories/category',
193 '/activity/glossary/onetest',
194 '/activity/glossary/othertest'));
be866f9d
EL
195 $this->assertTrue($pr instanceof progressive_parser_processor);
196 // Assign processor to parser
197 $pp->set_processor($pr);
198 // Set file from fixtures
bb7898c6 199 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test4.xml');
be866f9d
EL
200 // Process the file
201 $pp->process();
202 // Get processor debug info
203 $debug = $pr->debug_info();
204 $this->assertTrue(is_array($debug));
205 $this->assertTrue(array_key_exists('chunks', $debug));
206
207 // Check the number of chunks is correct for the file
bb7898c6 208 $this->assertEquals($debug['chunks'], 12);
be866f9d
EL
209 // Get all the simplified chunks and perform various validations
210 $chunks = $pr->get_chunks();
4c7f6ac6 211 // Check we have received the correct number of chunks
bb7898c6 212 $this->assertEquals(count($chunks), 12);
be866f9d
EL
213
214 // chunk[0] (/activity) tests
bb7898c6
PS
215 $this->assertEquals(count($chunks[0]), 3);
216 $this->assertEquals($chunks[0]['path'], '/activity');
217 $this->assertEquals($chunks[0]['level'],'2');
be866f9d 218 $tags = $chunks[0]['tags'];
bb7898c6
PS
219 $this->assertEquals(count($tags), 4);
220 $this->assertEquals($tags['id'], 1);
221 $this->assertEquals($tags['moduleid'], 5);
222 $this->assertEquals($tags['modulename'], 'glossary');
223 $this->assertEquals($tags['contextid'], 26);
224 $this->assertEquals($chunks[0]['level'],'2');
be866f9d
EL
225
226 // chunk[1] (/activity/glossary) tests
bb7898c6
PS
227 $this->assertEquals(count($chunks[1]), 3);
228 $this->assertEquals($chunks[1]['path'], '/activity/glossary');
229 $this->assertEquals($chunks[1]['level'],'3');
be866f9d 230 $tags = $chunks[1]['tags'];
bb7898c6
PS
231 $this->assertEquals(count($tags), 24);
232 $this->assertEquals($tags['id'], 1);
233 $this->assertEquals($tags['intro'], '<p>One simple glossary to test backup &amp; restore. Here it\'s the standard image:</p>'.
be866f9d
EL
234 "\n".
235 '<p><img src="@@PLUGINFILE@@/88_31.png" alt="pwd by moodle" width="88" height="31" /></p>');
bb7898c6 236 $this->assertEquals($tags['timemodified'], 1275639747);
be866f9d
EL
237 $this->assertTrue(!isset($tags['categories']));
238
239 // chunk[5] (second /activity/glossary/entries/entry) tests
bb7898c6
PS
240 $this->assertEquals(count($chunks[5]), 3);
241 $this->assertEquals($chunks[5]['path'], '/activity/glossary/entries/entry');
242 $this->assertEquals($chunks[5]['level'],'5');
be866f9d 243 $tags = $chunks[5]['tags'];
bb7898c6
PS
244 $this->assertEquals(count($tags), 15);
245 $this->assertEquals($tags['id'], 2);
246 $this->assertEquals($tags['concept'], 'cat');
be866f9d
EL
247 $this->assertTrue(!isset($tags['aliases']));
248 $this->assertTrue(!isset($tags['entries']));
249
250 // chunk[6] (second /activity/glossary/entries/entry/aliases/alias) tests
bb7898c6
PS
251 $this->assertEquals(count($chunks[6]), 3);
252 $this->assertEquals($chunks[6]['path'], '/activity/glossary/entries/entry/aliases/alias');
253 $this->assertEquals($chunks[6]['level'],'7');
be866f9d 254 $tags = $chunks[6]['tags'];
bb7898c6
PS
255 $this->assertEquals(count($tags), 2);
256 $this->assertEquals($tags['id'], 2);
257 $this->assertEquals($tags['alias_text'], 'cats');
be866f9d 258
4c7f6ac6 259 // chunk[7] (second /activity/glossary/entries/entry/aliases/alias) tests
bb7898c6
PS
260 $this->assertEquals(count($chunks[7]), 3);
261 $this->assertEquals($chunks[7]['path'], '/activity/glossary/entries/entry/aliases/alias');
262 $this->assertEquals($chunks[7]['level'],'7');
be866f9d 263 $tags = $chunks[7]['tags'];
bb7898c6
PS
264 $this->assertEquals(count($tags), 2);
265 $this->assertEquals($tags['id'], 3);
266 $this->assertEquals($tags['alias_text'], 'felines');
4c7f6ac6
EL
267
268 // chunk[8] (second /activity/glossary/entries/entry/ratings/rating) tests
bb7898c6
PS
269 $this->assertEquals(count($chunks[8]), 3);
270 $this->assertEquals($chunks[8]['path'], '/activity/glossary/entries/entry/ratings/rating');
271 $this->assertEquals($chunks[8]['level'],'7');
4c7f6ac6 272 $tags = $chunks[8]['tags'];
bb7898c6
PS
273 $this->assertEquals(count($tags), 6);
274 $this->assertEquals($tags['id'], 1);
275 $this->assertEquals($tags['timemodified'], '1275639779');
4c7f6ac6
EL
276
277 // chunk[9] (first /activity/glossary/onetest) tests
bb7898c6
PS
278 $this->assertEquals(count($chunks[9]), 3);
279 $this->assertEquals($chunks[9]['path'], '/activity/glossary/onetest');
280 $this->assertEquals($chunks[9]['level'],'4');
4c7f6ac6 281 $tags = $chunks[9]['tags'];
bb7898c6
PS
282 $this->assertEquals(count($tags), 2);
283 $this->assertEquals($tags['name'], 1);
284 $this->assertEquals($tags['value'], 1);
4c7f6ac6
EL
285
286 // chunk[10] (second /activity/glossary/onetest) tests
bb7898c6
PS
287 $this->assertEquals(count($chunks[10]), 3);
288 $this->assertEquals($chunks[10]['path'], '/activity/glossary/onetest');
289 $this->assertEquals($chunks[10]['level'],'4');
4c7f6ac6 290 $tags = $chunks[10]['tags'];
bb7898c6
PS
291 $this->assertEquals(count($tags), 2);
292 $this->assertEquals($tags['name'], 2);
293 $this->assertEquals($tags['value'], 2);
4c7f6ac6
EL
294
295 // chunk[11] (first /activity/glossary/othertest) tests
296 // note we don't allow repeated "final" element, so we only return the last one
bb7898c6
PS
297 $this->assertEquals(count($chunks[11]), 3);
298 $this->assertEquals($chunks[11]['path'], '/activity/glossary/othertest');
299 $this->assertEquals($chunks[11]['level'],'4');
4c7f6ac6 300 $tags = $chunks[11]['tags'];
bb7898c6
PS
301 $this->assertEquals(count($tags), 2);
302 $this->assertEquals($tags['name'], 4);
303 $this->assertEquals($tags['value'], 5);
14e6710d
EL
304
305 // Now check start notifications
306 $snotifs = $pr->get_start_notifications();
307 // Check we have received the correct number of notifications
bb7898c6 308 $this->assertEquals(count($snotifs), 12);
14e6710d 309 // Check first, sixth and last notifications
bb7898c6
PS
310 $this->assertEquals($snotifs[0], '/activity');
311 $this->assertEquals($snotifs[5], '/activity/glossary/entries/entry');
312 $this->assertEquals($snotifs[11], '/activity/glossary/othertest');
14e6710d
EL
313
314 // Now check end notifications
315 $enotifs = $pr->get_end_notifications();
316 // Check we have received the correct number of notifications
bb7898c6 317 $this->assertEquals(count($snotifs), 12);
14e6710d 318 // Check first, sixth and last notifications
bb7898c6
PS
319 $this->assertEquals($enotifs[0], '/activity/glossary/entries/entry/aliases/alias');
320 $this->assertEquals($enotifs[5], '/activity/glossary/entries/entry/ratings/rating');
321 $this->assertEquals($enotifs[11], '/activity');
14e6710d
EL
322
323 // Check start and end notifications are balanced
324 sort($snotifs);
325 sort($enotifs);
bb7898c6 326 $this->assertEquals($snotifs, $enotifs);
8298beda
EL
327
328 // Now verify that the start/process/end order is correct
329 $allnotifs = $pr->get_all_notifications();
bb7898c6 330 $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
8298beda
EL
331 // Check integrity of the notifications
332 $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
bb7898c6 333 $this->assertEquals($errcount, 0); // No errors found, plz
4c7f6ac6
EL
334 }
335
54b8f334
EL
336 /**
337 * test how the simplified processor and the order of start/process/end events happens
338 * with one real fragment of one backup 1.9 file, where some problems
339 * were found by David, hence we honor him in the name of the test ;-)
340 */
341 function test_simplified_david_backup19_file_fragment() {
342 global $CFG;
343 // Instantiate progressive_parser
344 $pp = new progressive_parser();
345 // Instantiate grouped_parser_processor
346 $pr = new mock_simplified_parser_processor();
347 // Add interesting paths
348 $pr->add_path('/MOODLE_BACKUP/COURSE');
349 $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
350 $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
351 $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
352 $this->assertTrue($pr instanceof progressive_parser_processor);
353 // Assign processor to parser
354 $pp->set_processor($pr);
355 // Set file from fixtures
bb7898c6 356 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test5.xml');
54b8f334
EL
357 // Process the file
358 $pp->process();
359
360 // Get all the simplified chunks and perform various validations
361 $chunks = $pr->get_chunks();
bb7898c6 362 $this->assertEquals(count($chunks), 3); // Only 3, because 7 (COURSE, ROLES_OVERRIDES and 5 MOD) are empty, aka no chunk
54b8f334
EL
363
364 // Now check start notifications
365 $snotifs = $pr->get_start_notifications();
366 // Check we have received the correct number of notifications
bb7898c6 367 $this->assertEquals(count($snotifs), 10); // Start tags are dispatched for empties (ROLES_OVERRIDES)
54b8f334 368 // Check first and last notifications
bb7898c6
PS
369 $this->assertEquals($snotifs[0], '/MOODLE_BACKUP/COURSE');
370 $this->assertEquals($snotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
371 $this->assertEquals($snotifs[2], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
372 $this->assertEquals($snotifs[3], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
373 $this->assertEquals($snotifs[7], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
374 $this->assertEquals($snotifs[8], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
375 $this->assertEquals($snotifs[9], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
54b8f334
EL
376
377 // Now check end notifications
378 $enotifs = $pr->get_end_notifications();
379 // Check we have received the correct number of notifications
bb7898c6 380 $this->assertEquals(count($snotifs), 10); // End tags are dispatched for empties (ROLES_OVERRIDES)
54b8f334 381 // Check first, and last notifications
bb7898c6
PS
382 $this->assertEquals($enotifs[0], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
383 $this->assertEquals($enotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
384 $this->assertEquals($enotifs[2], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
385 $this->assertEquals($enotifs[3], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
386 $this->assertEquals($enotifs[7], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
387 $this->assertEquals($enotifs[8], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
388 $this->assertEquals($enotifs[9], '/MOODLE_BACKUP/COURSE');
54b8f334
EL
389
390 // Check start and end notifications are balanced
391 sort($snotifs);
392 sort($enotifs);
bb7898c6 393 $this->assertEquals($snotifs, $enotifs);
54b8f334
EL
394
395 // Now verify that the start/process/end order is correct
396 $allnotifs = $pr->get_all_notifications();
bb7898c6 397 $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
54b8f334
EL
398 // Check integrity of the notifications
399 $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
bb7898c6 400 $this->assertEquals($errcount, 0); // No errors found, plz
54b8f334
EL
401 }
402
4c7f6ac6
EL
403 /*
404 * test progressive_parser parsing results using grouped_parser_processor and test4.xml
405 * (one simple glossary backup file example)
406 */
407 function test_grouped_parser_results() {
408 global $CFG;
409 // Instantiate progressive_parser
410 $pp = new progressive_parser();
411 // Instantiate grouped_parser_processor
412 $pr = new mock_grouped_parser_processor();
413 // Add interesting paths
414 $pr->add_path('/activity');
415 $pr->add_path('/activity/glossary', true);
416 $pr->add_path('/activity/glossary/entries/entry');
417 $pr->add_path('/activity/glossary/entries/entry/aliases/alias');
418 $pr->add_path('/activity/glossary/entries/entry/ratings/rating');
419 $pr->add_path('/activity/glossary/categories/category');
420 $pr->add_path('/activity/glossary/onetest');
421 $pr->add_path('/activity/glossary/othertest');
422 $this->assertTrue($pr instanceof progressive_parser_processor);
423 // Assign processor to parser
424 $pp->set_processor($pr);
425 // Set file from fixtures
bb7898c6 426 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test4.xml');
4c7f6ac6
EL
427 // Process the file
428 $pp->process();
429 // Get processor debug info
430 $debug = $pr->debug_info();
431 $this->assertTrue(is_array($debug));
432 $this->assertTrue(array_key_exists('chunks', $debug));
433
434 // Check the number of chunks is correct for the file
bb7898c6 435 $this->assertEquals($debug['chunks'], 2);
4c7f6ac6
EL
436 // Get all the simplified chunks and perform various validations
437 $chunks = $pr->get_chunks();
438 // Check we have received the correct number of chunks
bb7898c6 439 $this->assertEquals(count($chunks), 2);
4c7f6ac6
EL
440
441 // chunk[0] (/activity) tests
bb7898c6
PS
442 $this->assertEquals(count($chunks[0]), 3);
443 $this->assertEquals($chunks[0]['path'], '/activity');
444 $this->assertEquals($chunks[0]['level'],'2');
4c7f6ac6 445 $tags = $chunks[0]['tags'];
bb7898c6
PS
446 $this->assertEquals(count($tags), 4);
447 $this->assertEquals($tags['id'], 1);
448 $this->assertEquals($tags['moduleid'], 5);
449 $this->assertEquals($tags['modulename'], 'glossary');
450 $this->assertEquals($tags['contextid'], 26);
451 $this->assertEquals($chunks[0]['level'],'2');
4c7f6ac6
EL
452
453 // chunk[1] (grouped /activity/glossary tests)
bb7898c6
PS
454 $this->assertEquals(count($chunks[1]), 3);
455 $this->assertEquals($chunks[1]['path'], '/activity/glossary');
456 $this->assertEquals($chunks[1]['level'],'3');
4c7f6ac6 457 $tags = $chunks[1]['tags'];
bb7898c6
PS
458 $this->assertEquals(count($tags), 27);
459 $this->assertEquals($tags['id'], 1);
460 $this->assertEquals($tags['intro'], '<p>One simple glossary to test backup &amp; restore. Here it\'s the standard image:</p>'.
4c7f6ac6
EL
461 "\n".
462 '<p><img src="@@PLUGINFILE@@/88_31.png" alt="pwd by moodle" width="88" height="31" /></p>');
bb7898c6 463 $this->assertEquals($tags['timemodified'], 1275639747);
4c7f6ac6
EL
464 $this->assertTrue(!isset($tags['categories']));
465 $this->assertTrue(isset($tags['entries']));
466 $this->assertTrue(isset($tags['onetest']));
467 $this->assertTrue(isset($tags['othertest']));
468
469 // Various tests under the entries
470 $entries = $chunks[1]['tags']['entries']['entry'];
bb7898c6 471 $this->assertEquals(count($entries), 2);
4c7f6ac6
EL
472
473 // First entry
474 $entry1 = $entries[0];
bb7898c6
PS
475 $this->assertEquals(count($entry1), 17);
476 $this->assertEquals($entry1['id'], 1);
477 $this->assertEquals($entry1['userid'], 2);
478 $this->assertEquals($entry1['concept'], 'dog');
479 $this->assertEquals($entry1['definition'], '<p>Traditional enemies of cats</p>');
4c7f6ac6
EL
480 $this->assertTrue(isset($entry1['aliases']));
481 $this->assertTrue(isset($entry1['ratings']));
482 // aliases of first entry
483 $aliases = $entry1['aliases']['alias'];
bb7898c6 484 $this->assertEquals(count($aliases), 1);
4c7f6ac6
EL
485 // first alias
486 $alias1 = $aliases[0];
bb7898c6
PS
487 $this->assertEquals(count($alias1), 2);
488 $this->assertEquals($alias1['id'], 1);
489 $this->assertEquals($alias1['alias_text'], 'dogs');
4c7f6ac6
EL
490 // ratings of first entry
491 $ratings = $entry1['ratings']['rating'];
bb7898c6 492 $this->assertEquals(count($ratings), 1);
4c7f6ac6
EL
493 // first rating
494 $rating1 = $ratings[0];
bb7898c6
PS
495 $this->assertEquals(count($rating1), 6);
496 $this->assertEquals($rating1['id'], 2);
497 $this->assertEquals($rating1['value'], 6);
498 $this->assertEquals($rating1['timemodified'], '1275639797');
4c7f6ac6
EL
499
500 // Second entry
501 $entry2 = $entries[1];
bb7898c6
PS
502 $this->assertEquals(count($entry2), 17);
503 $this->assertEquals($entry2['id'], 2);
504 $this->assertEquals($entry2['userid'], 2);
505 $this->assertEquals($entry2['concept'], 'cat');
506 $this->assertEquals($entry2['definition'], '<p>traditional enemies of dogs</p>');
4c7f6ac6
EL
507 $this->assertTrue(isset($entry2['aliases']));
508 $this->assertTrue(isset($entry2['ratings']));
509 // aliases of first entry
510 $aliases = $entry2['aliases']['alias'];
bb7898c6 511 $this->assertEquals(count($aliases), 2);
4c7f6ac6
EL
512 // first alias
513 $alias1 = $aliases[0];
bb7898c6
PS
514 $this->assertEquals(count($alias1), 2);
515 $this->assertEquals($alias1['id'], 2);
516 $this->assertEquals($alias1['alias_text'], 'cats');
4c7f6ac6
EL
517 // second alias
518 $alias2 = $aliases[1];
bb7898c6
PS
519 $this->assertEquals(count($alias2), 2);
520 $this->assertEquals($alias2['id'], 3);
521 $this->assertEquals($alias2['alias_text'], 'felines');
4c7f6ac6
EL
522 // ratings of first entry
523 $ratings = $entry2['ratings']['rating'];
bb7898c6 524 $this->assertEquals(count($ratings), 1);
4c7f6ac6
EL
525 // first rating
526 $rating1 = $ratings[0];
bb7898c6
PS
527 $this->assertEquals(count($rating1), 6);
528 $this->assertEquals($rating1['id'], 1);
529 $this->assertEquals($rating1['value'], 5);
530 $this->assertEquals($rating1['scaleid'], 10);
4c7f6ac6
EL
531
532 // Onetest test (only 1 level nested)
533 $onetest = $tags['onetest'];
bb7898c6
PS
534 $this->assertEquals(count($onetest), 2);
535 $this->assertEquals(count($onetest[0]), 2);
536 $this->assertEquals($onetest[0]['name'], 1);
537 $this->assertEquals($onetest[0]['value'], 1);
538 $this->assertEquals(count($onetest[1]), 2);
539 $this->assertEquals($onetest[1]['name'], 2);
540 $this->assertEquals($onetest[1]['value'], 2);
4c7f6ac6
EL
541
542 // Other test (0 level nested, only last one is retrieved)
543 $othertest = $tags['othertest'];
bb7898c6
PS
544 $this->assertEquals(count($othertest), 1);
545 $this->assertEquals(count($othertest[0]), 2);
546 $this->assertEquals($othertest[0]['name'], 4);
547 $this->assertEquals($othertest[0]['value'], 5);
14e6710d
EL
548
549 // Now check start notifications
550 $snotifs = $pr->get_start_notifications();
551 // Check we have received the correct number of notifications
bb7898c6 552 $this->assertEquals(count($snotifs), 2);
14e6710d 553 // Check first and last notifications
bb7898c6
PS
554 $this->assertEquals($snotifs[0], '/activity');
555 $this->assertEquals($snotifs[1], '/activity/glossary');
14e6710d
EL
556
557 // Now check end notifications
558 $enotifs = $pr->get_end_notifications();
559 // Check we have received the correct number of notifications
bb7898c6 560 $this->assertEquals(count($snotifs), 2);
14e6710d 561 // Check first, and last notifications
bb7898c6
PS
562 $this->assertEquals($enotifs[0], '/activity/glossary');
563 $this->assertEquals($enotifs[1], '/activity');
14e6710d
EL
564
565 // Check start and end notifications are balanced
566 sort($snotifs);
567 sort($enotifs);
bb7898c6 568 $this->assertEquals($snotifs, $enotifs);
8298beda
EL
569
570 // Now verify that the start/process/end order is correct
571 $allnotifs = $pr->get_all_notifications();
bb7898c6 572 $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
8298beda
EL
573 // Check integrity of the notifications
574 $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
bb7898c6 575 $this->assertEquals($errcount, 0); // No errors found, plz
8298beda
EL
576 }
577
54b8f334
EL
578 /**
579 * test how the grouped processor and the order of start/process/end events happens
580 * with one real fragment of one backup 1.9 file, where some problems
581 * were found by David, hence we honor him in the name of the test ;-)
582 */
583 function test_grouped_david_backup19_file_fragment() {
584 global $CFG;
585 // Instantiate progressive_parser
586 $pp = new progressive_parser();
587 // Instantiate grouped_parser_processor
588 $pr = new mock_grouped_parser_processor();
589 // Add interesting paths
590 $pr->add_path('/MOODLE_BACKUP/COURSE');
591 $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION', true);
592 $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
593 $pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
594 $this->assertTrue($pr instanceof progressive_parser_processor);
595 // Assign processor to parser
596 $pp->set_processor($pr);
597 // Set file from fixtures
bb7898c6 598 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test5.xml');
54b8f334
EL
599 // Process the file
600 $pp->process();
601
602 // Get all the simplified chunks and perform various validations
603 $chunks = $pr->get_chunks();
bb7898c6 604 $this->assertEquals(count($chunks), 1); // Only 1, the SECTION one
54b8f334
EL
605
606 // Now check start notifications
607 $snotifs = $pr->get_start_notifications();
608 // Check we have received the correct number of notifications
bb7898c6 609 $this->assertEquals(count($snotifs), 2);
54b8f334 610 // Check first and last notifications
bb7898c6
PS
611 $this->assertEquals($snotifs[0], '/MOODLE_BACKUP/COURSE');
612 $this->assertEquals($snotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
54b8f334
EL
613
614 // Now check end notifications
615 $enotifs = $pr->get_end_notifications();
616 // Check we have received the correct number of notifications
bb7898c6 617 $this->assertEquals(count($snotifs), 2); // End tags are dispatched for empties (ROLES_OVERRIDES)
54b8f334 618 // Check first, and last notifications
bb7898c6
PS
619 $this->assertEquals($enotifs[0], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
620 $this->assertEquals($enotifs[1], '/MOODLE_BACKUP/COURSE');
54b8f334
EL
621
622 // Check start and end notifications are balanced
623 sort($snotifs);
624 sort($enotifs);
bb7898c6 625 $this->assertEquals($snotifs, $enotifs);
54b8f334
EL
626
627 // Now verify that the start/process/end order is correct
628 $allnotifs = $pr->get_all_notifications();
bb7898c6 629 $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
54b8f334
EL
630 // Check integrity of the notifications
631 $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
bb7898c6 632 $this->assertEquals($errcount, 0); // No errors found, plz
54b8f334
EL
633 }
634
98bba46b
DM
635 /**
636 */
637 function test_grouped_at_empty_node() {
638 global $CFG;
639 // Instantiate progressive_parser.
640 $pp = new progressive_parser();
641 // Instantiate grouped_parser_processor.
642 $pr = new mock_grouped_parser_processor();
643 $this->assertTrue($pr instanceof progressive_parser_processor);
644 // Add interesting paths - moodle1 style.
645 $pr->add_path('/test/MOODLE_BACKUP/COURSE/FORMATDATA', true);
646 $pr->add_path('/test/MOODLE_BACKUP/COURSE/FORMATDATA/WEEKS/WEEK');
647 $pr->add_path('/test/MOODLE_BACKUP/COURSE/EMPTYGROUPED', true);
648 $pr->add_path('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED', true);
649 $pr->add_path('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED/SUBS/SUB');
650 // Add interesting paths - moodle2 style.
651 $pr->add_path('/test/moodle2/grouped', true);
652 $pr->add_path('/test/moodle2/grouped/subs/sub');
653 $pr->add_path('/test/moodle2/groupedemptywithattr', true);
654 $pr->add_path('/test/moodle2/groupednonemptywithattr', true);
655 $pr->add_path('/test/moodle2/groupednonemptywithattr/subs/sub');
656 // Assign processor to parser.
657 $pp->set_processor($pr);
658 // Set file from fixtures.
659 $pp->set_file($CFG->dirroot . '/backup/util/xml/parser/tests/fixtures/test6.xml');
660 // Process the file.
661 $pp->process();
662
663 // Get all the simplified chunks and perform various validations.
664 $chunks = $pr->get_chunks();
665 $this->assertEquals(count($chunks), 6); // All grouped elements.
666
667 // Check some random data.
668 $this->assertEquals('/test/MOODLE_BACKUP/COURSE/FORMATDATA', $chunks[0]['path']);
669 $this->assertEquals(2, $chunks[0]['tags']['WEEKS']['WEEK'][1]['SECTION']);
670
671 $this->assertEquals('/test/MOODLE_BACKUP/COURSE/EMPTYGROUPED', $chunks[1]['path']);
672 $this->assertEquals(array(), $chunks[1]['tags']);
673
674 $this->assertEquals('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED', $chunks[2]['path']);
675 $this->assertEquals('Unit tests rock!', $chunks[2]['tags']['SUBS']['SUB'][0]['PROP']);
676
677 $this->assertEquals('/test/moodle2/grouped', $chunks[3]['path']);
678 $this->assertFalse(isset($chunks[3]['tags']['id'])); // No final elements, this should be fixed one day.
679 $this->assertEquals(34, $chunks[3]['tags']['subs']['sub'][0]['id']); // We have final element so this is parsed.
680 $this->assertEquals('Oh yeah', $chunks[3]['tags']['subs']['sub'][0]['prop']);
681
682 $this->assertEquals('/test/moodle2/groupednonemptywithattr', $chunks[4]['path']);
683 $this->assertEquals(78, $chunks[4]['tags']['id']); // We have final element so this is parsed.
684 $this->assertEquals('Go baby go', $chunks[4]['tags']['prop']);
685 $this->assertEquals(89, $chunks[4]['tags']['subs']['sub'][0]['id']);
686 $this->assertEquals('http://moodle.org', $chunks[4]['tags']['subs']['sub'][0]['prop']);
687
688 $this->assertEquals('/test/moodle2/groupedemptywithattr', $chunks[5]['path']);
689 $this->assertFalse(isset($chunks[5]['tags']['attr'])); // No final elements, this should be fixed one day.
690
691 // Now check start notifications.
692 $snotifs = $pr->get_start_notifications();
693 // Check we have received the correct number of notifications.
694 $this->assertEquals(count($snotifs), 6);
695 // Check the order of notifications (in order they appear in test6.xml).
696 $this->assertEquals('/test/MOODLE_BACKUP/COURSE/FORMATDATA', $snotifs[0]);
697 $this->assertEquals('/test/MOODLE_BACKUP/COURSE/EMPTYGROUPED', $snotifs[1]);
698 $this->assertEquals('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED', $snotifs[2]);
699 $this->assertEquals('/test/moodle2/grouped', $snotifs[3]);
700 $this->assertEquals('/test/moodle2/groupednonemptywithattr', $snotifs[4]);
701 $this->assertEquals('/test/moodle2/groupedemptywithattr', $snotifs[5]);
702
703 // Now check end notifications.
704 $enotifs = $pr->get_end_notifications();
705 // Check we have received the correct number of notifications.
706 $this->assertEquals(count($enotifs), 6);
707 // Check the order of notifications (in order they appear in test6.xml).
708 $this->assertEquals('/test/MOODLE_BACKUP/COURSE/FORMATDATA', $enotifs[0]);
709 $this->assertEquals('/test/MOODLE_BACKUP/COURSE/EMPTYGROUPED', $enotifs[1]);
710 $this->assertEquals('/test/MOODLE_BACKUP/COURSE/SECONDGROUPED', $enotifs[2]);
711 $this->assertEquals('/test/moodle2/grouped', $enotifs[3]);
712 $this->assertEquals('/test/moodle2/groupednonemptywithattr', $enotifs[4]);
713 $this->assertEquals('/test/moodle2/groupedemptywithattr', $enotifs[5]);
714
715 // Now verify that the start/process/end order is correct.
716 $allnotifs = $pr->get_all_notifications();
717 $this->assertEquals(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks));
718 // Check integrity of the notifications.
719 $errcount = $this->helper_check_notifications_order_integrity($allnotifs);
a3cdb2ba 720 $this->assertEquals(0, $errcount);
98bba46b 721 }
54b8f334 722
8298beda
EL
723 /**
724 * Helper function that given one array of ordered start/process/end notifications will
725 * check it of integrity like:
726 * - process only happens if start is the previous notification
727 * - end only happens if dispatch is the previous notification
728 * - start only happen with level > than last one and if there is no already started like that
729 *
730 * @param array $notifications ordered array of notifications with format [start|process|end]:path
731 * @return int number of integrity problems found (errors)
732 */
733 function helper_check_notifications_order_integrity($notifications) {
734 $numerrors = 0;
735 $notifpile = array('pilebase' => 'start');
d4dd0659 736 $lastnotif = 'start:pilebase';
8298beda 737 foreach ($notifications as $notif) {
8298beda 738
d4dd0659
EL
739 $lastpiletype = end($notifpile);
740 $lastpilepath = key($notifpile);
741 $lastpilelevel = strlen(preg_replace('/[^\/]/', '', $lastpilepath));
742
743 $lastnotiftype = preg_replace('/:.*/', '', $lastnotif);
744 $lastnotifpath = preg_replace('/.*:/', '', $lastnotif);
745 $lastnotiflevel = strlen(preg_replace('/[^\/]/', '', $lastnotifpath));
746
8298beda
EL
747 $notiftype = preg_replace('/:.*/', '', $notif);
748 $notifpath = preg_replace('/.*:/', '', $notif);
d4dd0659 749 $notiflevel = strlen(preg_replace('/[^\/]/', '', $notifpath));
8298beda
EL
750
751 switch ($notiftype) {
752 case 'process':
d4dd0659
EL
753 if ($lastnotifpath != $notifpath or $lastnotiftype != 'start') {
754 $numerrors++; // Only start for same path from last notification is allowed before process
8298beda
EL
755 }
756 $notifpile[$notifpath] = 'process'; // Update the status in the pile
757 break;
758 case 'end':
54b8f334 759 if ($lastpilepath != $notifpath or ($lastpiletype != 'process' and $lastpiletype != 'start')) {
d4dd0659 760 $numerrors++; // Only process and start for same path from last pile is allowed before end
8298beda
EL
761 }
762 unset($notifpile[$notifpath]); // Delete from the pile
763 break;
764 case 'start':
765 if (array_key_exists($notifpath, $notifpile) or $notiflevel <= $lastpilelevel) {
d4dd0659 766 $numerrors++; // Only non existing in pile and with level > last pile is allowed on start
8298beda
EL
767 }
768 $notifpile[$notifpath] = 'start'; // Add to the pile
769 break;
770 default:
771 $numerrors++; // Incorrect type of notification => error
772 }
d4dd0659
EL
773 // Update lastnotif
774 $lastnotif = $notif;
8298beda
EL
775 }
776 return $numerrors;
be866f9d
EL
777 }
778}
779
780/*
781 * helper processor able to perform various auto-cheks based on attributes while processing
782 * the test1.xml file available in the fixtures dir. It performs these checks:
783 * - name equal to "name" attribute of the tag (if present)
784 * - level equal to "level" attribute of the tag (if present)
785 * - path + tagname equal to "path" attribute of the tag (if present)
786 * - cdata, if not empty is:
787 * - equal to "value" attribute of the tag (if present)
788 * - else, equal to tag name
789 *
790 * We pass the whole UnitTestCase object to the processor in order to be
791 * able to perform the tests in the straight in the process
792 */
793class mock_auto_parser_processor extends progressive_parser_processor {
794
795 private $utc = null; // To store the unit test case
796
797 public function __construct($unit_test_case) {
798 parent::__construct();
799 $this->utc = $unit_test_case;
800 }
801
802 public function process_chunk($data) {
803 // Perform auto-checks based in the rules above
804 if (isset($data['tags'])) {
805 foreach ($data['tags'] as $tag) {
806 if (isset($tag['attrs']['name'])) { // name tests
bb7898c6 807 $this->utc->assertEquals($tag['name'], $tag['attrs']['name']);
be866f9d
EL
808 }
809 if (isset($tag['attrs']['level'])) { // level tests
bb7898c6 810 $this->utc->assertEquals($data['level'], $tag['attrs']['level']);
be866f9d
EL
811 }
812 if (isset($tag['attrs']['path'])) { // path tests
bb7898c6 813 $this->utc->assertEquals(rtrim($data['path'], '/') . '/' . $tag['name'], $tag['attrs']['path']);
be866f9d
EL
814 }
815 if (!empty($tag['cdata'])) { // cdata tests
816 if (isset($tag['attrs']['value'])) {
bb7898c6 817 $this->utc->assertEquals($tag['cdata'], $tag['attrs']['value']);
be866f9d 818 } else {
bb7898c6 819 $this->utc->assertEquals($tag['cdata'], $tag['name']);
be866f9d
EL
820 }
821 }
822 }
823 }
824 }
825}
826
827/*
828 * helper processor that accumulates all the chunks, resturning them with the get_chunks() method
829 */
830class mock_parser_processor extends progressive_parser_processor {
831
832 private $chunksarr = array(); // To accumulate the found chunks
833
834 public function process_chunk($data) {
835 $this->chunksarr[] = $data;
836 }
837
838 public function get_chunks() {
839 return $this->chunksarr;
840 }
841}
842
843/*
844 * helper processor that accumulates simplified chunks, returning them with the get_chunks() method
845 */
846class mock_simplified_parser_processor extends simplified_parser_processor {
847
848 private $chunksarr = array(); // To accumulate the found chunks
14e6710d
EL
849 private $startarr = array(); // To accumulate all the notified path starts
850 private $endarr = array(); // To accumulate all the notified path ends
8298beda 851 private $allnotif = array(); // To accumulate all the notified and dispatched events in an ordered way
be866f9d
EL
852
853 public function dispatch_chunk($data) {
854 $this->chunksarr[] = $data;
8298beda 855 $this->allnotif[] = 'process:' . $data['path'];
be866f9d
EL
856 }
857
14e6710d
EL
858 public function notify_path_start($path) {
859 $this->startarr[] = $path;
8298beda 860 $this->allnotif[] = 'start:' . $path;
14e6710d
EL
861 }
862
863 public function notify_path_end($path) {
864 $this->endarr[] = $path;
8298beda 865 $this->allnotif[] = 'end:' . $path;
14e6710d
EL
866 }
867
be866f9d
EL
868 public function get_chunks() {
869 return $this->chunksarr;
870 }
14e6710d
EL
871
872 public function get_start_notifications() {
873 return $this->startarr;
874 }
875
876 public function get_end_notifications() {
877 return $this->endarr;
878 }
8298beda
EL
879
880 public function get_all_notifications() {
881 return $this->allnotif;
882 }
be866f9d 883}
4c7f6ac6
EL
884
885/*
886 * helper processor that accumulates grouped chunks, returning them with the get_chunks() method
887 */
888class mock_grouped_parser_processor extends grouped_parser_processor {
889
890 private $chunksarr = array(); // To accumulate the found chunks
14e6710d
EL
891 private $startarr = array(); // To accumulate all the notified path starts
892 private $endarr = array(); // To accumulate all the notified path ends
8298beda 893 private $allnotif = array(); // To accumulate all the notified and dispatched events in an ordered way
4c7f6ac6
EL
894
895 public function dispatch_chunk($data) {
896 $this->chunksarr[] = $data;
8298beda 897 $this->allnotif[] = 'process:' . $data['path'];
4c7f6ac6
EL
898 }
899
14e6710d
EL
900 public function notify_path_start($path) {
901 $this->startarr[] = $path;
8298beda 902 $this->allnotif[] = 'start:' . $path;
14e6710d
EL
903 }
904
905 public function notify_path_end($path) {
906 $this->endarr[] = $path;
8298beda 907 $this->allnotif[] = 'end:' . $path;
14e6710d
EL
908 }
909
4c7f6ac6
EL
910 public function get_chunks() {
911 return $this->chunksarr;
912 }
14e6710d
EL
913
914 public function get_start_notifications() {
915 return $this->startarr;
916 }
917
918 public function get_end_notifications() {
919 return $this->endarr;
920 }
8298beda
EL
921
922 public function get_all_notifications() {
923 return $this->allnotif;
924 }
4c7f6ac6 925}